《Java-SE-第二十六章》之线程池

简介: 《Java-SE-第二十六章》之线程池

文章目录

线程池概述

什么是线程池?

为什么从线程池拿会比直接创建线程快?

Java标准库中的线程池

四种拒接策略演示

Executors

简单实现线程池

线程池的基本逻辑

实现线程池的基本逻辑

线程池概述

什么是线程池?

 线程虽然是轻量级进程,尽管线程比进程创建和销毁所消耗 的资源要少。但是如果线程的创建和销毁频率高了,开销也还是有的,为了进一步提高效率,引入了线程池,池子里面放着事先创建好的线程.后面用的时候直接从池子里面拿,如此速度就快了,但是代价线程池所需的空间,线程池就是以空间换时间。

为什么从线程池拿会比直接创建线程快?

 因为创建线程和销毁线程是操作系统完成了,需要从用户态切换到内核态 这是耗时耗力 的。如果从线程池直接拿的话,就省去了切换到内核态的时间,同时当线程不用的时候直接放回到线程池即可。

Java标准库中的线程池

 标准库中线程池为ThreadPoolExecutor类,该类中最主要是包含两类线程,一类是核心线程,另一类是非核心线程。当派发任务给线程池中的线程时,干活的是核心线程,当来的活太多了,核心线程不够用了,就会启动非核心线程。当活变少了,就会把非核心线程 给裁了。简单来说所谓的核心线程就像公司里面的正式工,非核心线程则是实习生。当公司人手不够的时候就会招多点实习生来干活,当活少了,实习生也就可以走了。

在Java8中,ThreadPoolExecutor一共提供了4个构造方法,在此主要介绍参数最多的,其他的三个构造方法都是这个构造方法减少参数而来的,所以搞懂了这个参数最多的构造方法,其他的自然而然也明白了。

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

参数解释

corePoolSize:表示核心线程数

maximumPoolSize:池中允许的最大线程数,就是核心线程和非核心线程之和

keepAliveTime:非核心线程在被终止之前等待新任务的最大时间,超过这个时间,该线程就会被停用。

unit:时间单位

workQueue:在任务执行之前用于保存任务的队列,该队列仅将保存submit方法提交的Runnable任务

threadFactory:创建新线程 时所使用的工厂

RejectedExecutionHandler:拒绝策略,执行被处理使用的处理程序,因为达到线程限制和对列容量

拒接策略详解

ThreadPoolExecutor中有四个静态内部类实现了RejectedExecutionHandler接口,分别对应四种不同的拒绝策略

AbortPolicy:被拒绝的任务的处理程序,抛出一个 RejectedExecutionException 。当活太多了,线程已经忙不过来了,还来活时,直接不处理,抛出异常。

CallerRunsPolicy:任务从哪里来就回到哪里去。

DiscardOldestPolicy:队列满了但是不会抛出异常,直接丢弃新任务,不做任何处理

DiscardPolicy:队列满了, 丢弃工作队列中最旧的任务,然后尝试再次提交新任务,不会抛出异常。

常用方法

四种拒接策略演示

AbortPolicy

演示代码

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorDemo {
    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor threadExecutor = new ThreadPoolExecutor(
                1,
                1,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(2),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
        for (int i = 0; i < 5; i++) {
            final int taskId = i;
            threadExecutor.submit(()->someTask(taskId));
        }
        threadExecutor.shutdown();
    }
    /**
     * 定义一个需要并发执行的任务
     *
     * @param taskId
     */
    private static void someTask(int taskId) {
        System.out.println("Task " + taskId + " is starting...");
        try {
            Thread.sleep(100); // 模拟任务执行时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Task " + taskId + " is finished!");
    }
}

运行结果:

任务太多了,抛出异常之后就罢工了,不干活了。

CallerRunsPolicy

演示代码

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorDemo {
    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor threadExecutor = new ThreadPoolExecutor(
                1,
                1,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(2),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
        for (int i = 0; i < 5; i++) {
            final int taskId = i;
            threadExecutor.submit(()->someTask(Thread.currentThread().getName(),taskId));
        }
        threadExecutor.shutdown();
    }
    /**
     * 定义一个需要并发执行的任务
     *
     * @param taskId
     */
    private static void someTask(String name,int taskId) {
        System.out.println(name+":Task " + taskId + " is starting...");
        try {
            Thread.sleep(100); // 模拟任务执行时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Task " + taskId + " is finished!");
    }
}

运行结果:

当任务过多时,直接拒接不干了,要干你自己干,所以有部分任务是main线程自己干的

DiscardOldestPolicy

代码演示

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorDemo {
    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor threadExecutor = new ThreadPoolExecutor(
                1,
                1,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(1),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy()
        );
        for (int i = 0; i < 5; i++) {
            final int taskId = i;
            threadExecutor.submit(()->someTask(Thread.currentThread().getName(),taskId));
        }
        threadExecutor.shutdown();
    }
    /**
     * 定义一个需要并发执行的任务
     *
     * @param taskId
     */
    private static void someTask(String name,int taskId) {
        System.out.println(name+":Task " + taskId + " is starting...");
        try {
            Thread.sleep(100); // 模拟任务执行时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Task " + taskId + " is finished!");
    }
}

运行结果:

DiscardPolicy

代码演示

import java.util.concurrent.*;
public class ThreadPoolExecutorDemo {
    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor threadExecutor = new ThreadPoolExecutor(
                2,
                2,
                3,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(1),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardPolicy()
        );
        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            threadExecutor.submit(()->someTask(Thread.currentThread().getName(),taskId));
        }
        threadExecutor.shutdown();
    }
    /**
     * 定义一个需要并发执行的任务
     *
     * @param taskId
     */
    private static void someTask(String name,int taskId) {
        System.out.println(name+":Task " + taskId + " is starting...");
        try {
            Thread.sleep(2000); // 模拟任务执行时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Task " + taskId + " is finished!");
    }
}

运行结果:

Executors

如果你觉得上述创建线程池的方式太复杂了,可以使用Executors来创建线程,其返回值是ExecutorService接口。Executors 本质上是 ThreadPoolExecutor 类的封装.

Executors 创建线程池的几种方式

newFixedThreadPool: 创建固定线程数的线程池

newCachedThreadPool: 创建线程数目动态增长的线程池.

newSingleThreadExecutor: 创建只包含单个线程的线程池.

newScheduledThreadPool: 设定 延迟时间后执行命令,或者定期执行命令. 是进阶版的 Timer.

使用演示

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorsDemo {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            final int taskId = i;
            executorService.submit(()->someTask(Thread.currentThread().getName(),taskId));
        }
        executorService.shutdown();
    }
    /**
     * 定义一个需要并发执行的任务
     *
     * @param taskId
     */
    private static void someTask(String name,int taskId) {
        System.out.println(name+":Task " + taskId + " is starting...");
        try {
            Thread.sleep(2000); // 模拟任务执行时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Task " + taskId + " is finished!");
    }
}

运行结果:

简单实现线程池

线程池的基本逻辑

 线程池事先存放着准备好的线程,当有任务提交入池的时候,实际上是放入了阻塞队列中,然后线程池中的线程调度执行这些任务,在java中的线程池有核心线程和非核心线程,我们是简单实现,所以都是以核心线程的方式实现。

实现线程池的基本逻辑

 使用阻塞队列组织所有的任务,定义一个线程池类其核心方法为submit()将任务添加到阻塞队列中,还需要一个工作线程不断向阻塞对列扫描获取任务并执行任务。

实现代码

MyThreadPool类实现

import java.util.concurrent.LinkedBlockingQueue;
public class MyThreadPool {
    private int maxWorkerCount = 10;
    private LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue();
    public void submit(Runnable command) throws InterruptedException {
        if (queue.size() < maxWorkerCount) {
            // 当前 worker 数不足, 就继续创建 worker
            Worker worker = new Worker(queue);
            worker.start();
        }
        // 将任务添加到任务队列中
        queue.put(command);
    }
}

Worker实现

import java.util.concurrent.LinkedBlockingQueue;
public class Worker extends Thread {
    private LinkedBlockingQueue<Runnable> queue = null;
    public Worker(LinkedBlockingQueue<Runnable> queue) {
        super("worker");
        this.queue = queue;
    }
    @Override
    public void run() {
        // try 必须放在 while 外头, 或者 while 里头应该影响不大
        try {
            while (!Thread.interrupted()) {
                Runnable runnable = queue.take();
                runnable.run();
            }
        } catch (InterruptedException e) {
        }
    }
}

测试代码

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        MyThreadPool myThreadPool = new MyThreadPool();
        for (int i = 0; i < 5; i++) {
            final int taskId = i;
            myThreadPool.submit(() -> someTask(Thread.currentThread().getName(),taskId));
        }
        Thread.sleep(1000);
    }
    /**
     * 定义一个需要并发执行的任务
     *
     * @param taskId
     */
    private static void someTask(String name,int taskId) {
        System.out.println(name+":Task " + taskId + " is starting...");
        try {
            Thread.sleep(2000); // 模拟任务执行时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Task " + taskId + " is finished!");
    }
}

运行结果:


 各位看官如果觉得文章写得不错,点赞评论关注走一波!谢谢啦!。

相关文章
|
6天前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)
|
17天前
|
监控 Java 调度
【Java学习】多线程&JUC万字超详解
本文详细介绍了多线程的概念和三种实现方式,还有一些常见的成员方法,CPU的调动方式,多线程的生命周期,还有线程安全问题,锁和死锁的概念,以及等待唤醒机制,阻塞队列,多线程的六种状态,线程池等
79 6
【Java学习】多线程&JUC万字超详解
|
2天前
|
Java
深入理解Java中的多线程编程
本文将探讨Java多线程编程的核心概念和技术,包括线程的创建与管理、同步机制以及并发工具类的应用。我们将通过实例分析,帮助读者更好地理解和应用Java多线程编程,提高程序的性能和响应能力。
15 4
|
10天前
|
Java 调度 开发者
Java并发编程:深入理解线程池
在Java的世界中,线程池是提升应用性能、实现高效并发处理的关键工具。本文将深入浅出地介绍线程池的核心概念、工作原理以及如何在实际应用中有效利用线程池来优化资源管理和任务调度。通过本文的学习,读者能够掌握线程池的基本使用技巧,并理解其背后的设计哲学。
|
2天前
|
安全 Java 调度
Java 并发编程中的线程安全和性能优化
本文将深入探讨Java并发编程中的关键概念,包括线程安全、同步机制以及性能优化。我们将从基础入手,逐步解析高级技术,并通过实例展示如何在实际开发中应用这些知识。阅读完本文后,读者将对如何在多线程环境中编写高效且安全的Java代码有一个全面的了解。
|
10天前
|
缓存 监控 Java
Java中的并发编程:理解并应用线程池
在Java的并发编程中,线程池是提高应用程序性能的关键工具。本文将深入探讨如何有效利用线程池来管理资源、提升效率和简化代码结构。我们将从基础概念出发,逐步介绍线程池的配置、使用场景以及最佳实践,帮助开发者更好地掌握并发编程的核心技巧。
|
7天前
|
Java 调度 开发者
Java中的多线程基础及其应用
【9月更文挑战第13天】本文将深入探讨Java中的多线程概念,从基本理论到实际应用,带你一步步了解如何有效使用多线程来提升程序的性能。我们将通过实际代码示例,展示如何在Java中创建和管理线程,以及如何利用线程池优化资源管理。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧,帮助你更好地理解和应用多线程编程。
|
12天前
|
缓存 监控 Java
java中线程池的使用
java中线程池的使用
|
11天前
|
算法 Java 数据处理
Java并发编程:解锁多线程的力量
在Java的世界里,掌握并发编程是提升应用性能和响应能力的关键。本文将深入浅出地探讨如何利用Java的多线程特性来优化程序执行效率,从基础的线程创建到高级的并发工具类使用,带领读者一步步解锁Java并发编程的奥秘。你将学习到如何避免常见的并发陷阱,并实际应用这些知识来解决现实世界的问题。让我们一起开启高效编码的旅程吧!
|
16天前
|
存储 Java 程序员
优化Java多线程应用:是创建Thread对象直接调用start()方法?还是用个变量调用?
这篇文章探讨了Java中两种创建和启动线程的方法,并分析了它们的区别。作者建议直接调用 `Thread` 对象的 `start()` 方法,而非保持强引用,以避免内存泄漏、简化线程生命周期管理,并减少不必要的线程控制。文章详细解释了这种方法在使用 `ThreadLocal` 时的优势,并提供了代码示例。作者洛小豆,文章来源于稀土掘金。