Java多线程 ThreadPoolExecutor自定义线程池

简介: Java多线程 ThreadPoolExecutor自定义线程池

一、说明


ThreadPoolExecutor



  • 它有多个构造方法来实现自定义创建线程池,以内部线程池的形式对外提供管理任务执行,线程调度,线程池管理等



二、理解


ThreadPoolExecutor


java.util.cocurrent 包下ThreadPoolExecutor类继承AbstractExecutorService


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


  • corePoolSize 核心线程数,默认为0,有任务时将创建线程去执行,当线程数达到corePoolSize时,停止线程创建,将任务放到队列中等待;调用prestartAllCoreThreads()prestartCoreThread()方法,可以预创建线程


  • maximumPoolSize 线程池线程数,当线程数达到核corePoolSize时,如果任务队列已满,线程池会创建新的线程,直到线程数量达到最maxPoolSize


  • keepAliveTime 线程池中超过corePoolSize的空闲线程最大存活时间,超时则销毁,直到线程数量等于corePoolSize


  • TimeUnit keepAliveTime时间单位


  • workQueue 阻塞任务队列,用来存储等待执行的任务


  • threadFactory 线程工厂,可以更改线程的名称,线程组,优先级,守护进程状态等


  • RejectedExecutionHandler 拒绝执行策略,当提交任务数超过maximumPoolSize+workQueue时,再提交任务的会交给拒绝策略去处理


workQueue


  • 当线程池任务线程数量 < corePoolSize,执行器会创建一个新的线程来执行新添加的任务


  • 当线程池任务线程数量 > corePoolSize,且workQueue未满时,执行器将新添加的任务放到workQueue中


  • 当线程池任务线程数量 > corePoolSize,且workQueue已满时,行器会创建一个新的线程来执行新添加的任务,直到超过maximumPoolSize执行拒绝策略


队列策略


  • Direct handoffs 直接握手队列,使用SynchronousQueue队列,提交的任务会马上执行,不会被保存,如果执行任务线程数小于maximumPoolSize,则尝试创建新的进程,如果达到maximumPoolSize,则执行拒绝策略


  • Bounded queues 有界队列,一般使用ArrayBlockingQueue制定队列的最大长度,当创建线程数达到corePoolSize时,若有新任务加入,则直接进入任务队列等待,若等待队列已满,即超过ArrayBlockingQueue初始化的容量,则继续创建线程,直到线程数达到maximumPoolSize,则执行拒绝策略


  • Unbounded queues 无界队列,一般使用无预定长度的LinkedBlockingQueue,当线程数达到corePoolSize后,若有新任务加入,则直接进入任务队列等待,任务队列可以无限添加新的任务,maximumPoolSize参数是无效的


RejectedExecutionHandler


  • 当线程池已经被关闭,或者任务数超过maximumPoolSize+workQueue时执行拒绝策略


  • ThreadPoolExecutor.AbortPolicy 默认拒绝策略,丢弃任务并抛出RejectedExecutionException异常


  • ThreadPoolExecutor.DiscardPolicy 直接丢弃任务,但不抛出异常


  • ThreadPoolExecutor.DiscardOldestPolicy 丢弃任务队列最先加入的任务,再执行execute方法把新任务加入队列执行


  • ThreadPoolExecutor.CallerRunsPolicy 会调用当前线程池的所在的线程去执行被拒绝的任务


线程池任务的执行流程



三、实现


1.SynchronousQueue


创建 ThreadPoolExecutorTest类,默认使用ThreadPoolExecutor.AbortPolicy拒绝策略,队列是SynchronousQueue,超出核心线程的任务会创建新的线程来执行,设置核心线程数最大值为4,线程池线程数最大值为8,最大等待时间为5秒


public class ThreadPoolExecutorTest {
    public static void main(String[] args) throws InterruptedException {
        // 1.创建自定义线程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 8, 5, 
        TimeUnit.SECONDS, 
        new SynchronousQueue<>(),
        Executors.defaultThreadFactory(),
        new ThreadPoolExecutor.AbortPolicy());
        // 2.创建线程任务
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                    System.out.println(Thread.currentThread().getName() + " run");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        // 3.执行任务
        System.out.println("设置核心线程数最大值为4,线程池线程数最大值为8");
        System.out.println("&&&&开始执行线程&&&&");
        System.out.println("----执行4个任务----");
        threadPoolExecutor.execute(runnable);
        threadPoolExecutor.execute(runnable);
        threadPoolExecutor.execute(runnable);
        threadPoolExecutor.execute(runnable);
        System.out.println("当前核心线程数" + threadPoolExecutor.getCorePoolSize());
        System.out.println("当前线程池线程数" + threadPoolExecutor.getPoolSize());
        System.out.println("当前队列任务数" + threadPoolExecutor.getQueue().size());
        System.out.println("----再执行4个任务----");
        threadPoolExecutor.execute(runnable);
        threadPoolExecutor.execute(runnable);
        threadPoolExecutor.execute(runnable);
        threadPoolExecutor.execute(runnable);
        System.out.println("当前核心线程数" + threadPoolExecutor.getCorePoolSize());
        System.out.println("当前线程池线程数" + threadPoolExecutor.getPoolSize());
        System.out.println("当前队列任务数" + threadPoolExecutor.getQueue().size());
        Thread.sleep(10000);
        System.out.println("----休眠10秒后----");
        System.out.println("当前核心线程数" + threadPoolExecutor.getCorePoolSize());
        System.out.println("当前线程池线程数" + threadPoolExecutor.getPoolSize());
        System.out.println("当前队列任务数" + threadPoolExecutor.getQueue().size());
        // 4.关闭线程池
        threadPoolExecutor.shutdown();
    }
}


一共有4个核心,超过核心线程将创建非核心线程,核心线程默认情况下不会被回收,不受时间限制,而超时的非核心线程将被回收



但如果再执行4个任务,线程数超过maximumPoolSize,再提交任务将被丢弃并抛出RejectedExecutionException异常



2.ArrayBlockingQueue


当线程数达到corePoolSize后,若有新任务加入,则直接进入任务队列等待,超出队列的任务会创建新的线程来执行


        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 8, 5, 
        TimeUnit.SECONDS, 
        new ArrayBlockingQueue<>(4),
        Executors.defaultThreadFactory(),
        new ThreadPoolExecutor.AbortPolicy());


一共有4个核心,当线程数超过corePoolSize+workQueue时,将创建非核心线程,核心线程默认情况下不会被回收,不受时间限制,而超时的非核心线程将被回收



但如果再执行4个任务,线程数超过maximumPoolSize+workQueue,再提交任务将被丢弃并抛出RejectedExecutionException异常



3.LinkedBlockingQueue


当线程数达到corePoolSize后,若有新任务加入,则直接进入任务队列等待,任务队列可以无限添加新的任务,maximumPoolSize参数是无效的


        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 8, 5, 
        TimeUnit.SECONDS, 
        new LinkedBlockingDeque<>(), 
        Executors.defaultThreadFactory(),
        new ThreadPoolExecutor.AbortPolicy());    


一共有4个核心,超过核心线程将创建非核心线程,核心线程默认情况下不会被回收,不受时间限制,而超时的非核心线程将被回收



但当设置LinkedBlockingDeque容量限制为4时,当线程数超过corePoolSize+workQueue时,将创建非核心线程,核心线程默认情况下不会被回收,不受时间限制,而超时的非核心线程将被回收


        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 8, 5, 
        TimeUnit.SECONDS, 
        new LinkedBlockingDeque<>(4), 
        Executors.defaultThreadFactory(),
        new ThreadPoolExecutor.AbortPolicy());



但如果再执行4个任务,线程数超过maximumPoolSize+workQueue,再提交任务将被丢弃并抛出RejectedExecutionException异常



目录
相关文章
|
27天前
|
监控 安全 Java
在 Java 中使用线程池监控以及动态调整线程池时需要注意什么?
【10月更文挑战第22天】在进行线程池的监控和动态调整时,要综合考虑多方面的因素,谨慎操作,以确保线程池能够高效、稳定地运行,满足业务的需求。
104 38
|
26天前
|
Java
线程池内部机制:线程的保活与回收策略
【10月更文挑战第24天】 线程池是现代并发编程中管理线程资源的一种高效机制。它不仅能够复用线程,减少创建和销毁线程的开销,还能有效控制并发线程的数量,提高系统资源的利用率。本文将深入探讨线程池中线程的保活和回收机制,帮助你更好地理解和使用线程池。
52 2
|
27天前
|
Prometheus 监控 Cloud Native
JAVA线程池监控以及动态调整线程池
【10月更文挑战第22天】在 Java 中,线程池的监控和动态调整是非常重要的,它可以帮助我们更好地管理系统资源,提高应用的性能和稳定性。
65 4
|
27天前
|
Prometheus 监控 Cloud Native
在 Java 中,如何使用线程池监控以及动态调整线程池?
【10月更文挑战第22天】线程池的监控和动态调整是一项重要的任务,需要我们结合具体的应用场景和需求,选择合适的方法和策略,以确保线程池始终处于最优状态,提高系统的性能和稳定性。
109 2
|
30天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
19 3
|
30天前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
19 2
|
30天前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
30 2
|
30天前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
34 1
|
Java 安全 数据库
java多线程系列:ThreadPoolExecutor源码分析
前言 这篇主要讲述ThreadPoolExecutor的源码分析,贯穿类的创建、任务的添加到线程池的关闭整个流程,让你知其然所以然。希望你可以通过本篇博文知道ThreadPoolExecutor是怎么添加任务、执行任务的,以及延伸的知识点。
1194 0
|
8天前
|
Java 开发者
Java多线程编程中的常见误区与最佳实践####
本文深入剖析了Java多线程编程中开发者常遇到的几个典型误区,如对`start()`与`run()`方法的混淆使用、忽视线程安全问题、错误处理未同步的共享变量等,并针对这些问题提出了具体的解决方案和最佳实践。通过实例代码对比,直观展示了正确与错误的实现方式,旨在帮助读者构建更加健壮、高效的多线程应用程序。 ####
下一篇
无影云桌面