JUC—Executor线程池框架源码深度解析六万字
Posted 刘Java
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JUC—Executor线程池框架源码深度解析六万字相关的知识,希望对你有一定的参考价值。
基于JDK1.8详细介绍了Executor线程池框架的基本架构组成,以及ThreadPoolExecutor、FutureTask、ScheduledThreadPoolExecutor等核心类的主要实现原理!
文章目录
- 1 Executor框架的概述
- 2 Executor线程池的概述
- 3 Executor线程池的基本结构
- 4 ThreadPoolExecutor 线程池实现
- 4.1 ThreadPoolExecutor的概述
- 4.2 ThreadPoolExecutor的主要属性
- 4.3 ThreadPoolExecutor的构造器
- 4.4 execute核心提交方法
- 4.5 submit提交方法
- 4.6 核心线程预启动
- 4.7 关闭线程池
- 4.8 hook钩子方法
- 4.9 线程池信息获取
- 4.10 设置线程池参数
- 5 ScheduledThreadPoolExecutor 延迟线程池
- 6 Executors 线程池工厂
- 7 总结
1 Executor框架的概述
JDK1.5之前,我们如果想要使用Java线程来完成相关任务,一般涉及两个类,一个是Thread类,一个Thread对象在启动(start)之后会创建一个关联的本地操作系统线程,随后会自动回调run方法。另一个是Runnable接口,可以看作 run方法的抽象,代表线程任务。通过Runnable和Thread的配合可以编写我们自己的多线程逻辑。
可以看到,此时Java对于多线程编程的支持还是比较原始的,功能也并不多。因此,在JDK1.5的JUC包中,对Java的多线程应用做了一次全面的扩展,比如新lock锁、并发容器等,还有一个重要的扩展就是出现了Executor执行框架。
Executor执行框架将Java线程的应用做了更细致的功能划分,并且进行了功能的增强,大概包括三个部分:
- 线程任务
- JDK1.5之前,只有Runnable代表线程任务,对于受检异常,必须手动在try catch中处理,不支持throws声明可能抛出的异常,不支持任务返回值。
- JDK1.5的时候,出现了Callable接口,可以看作Runnable的增强:对于受检异常,可以不用在try catch中处理,支持throws声明可能抛出的异常,并且支持任务返回值。
- 执行器
- JDK1.5之前,线程任务的执行需要我们手动创建Thread对象、传入任务、并调用start方法,一个任务对应一个线程,它们之间的关联非常紧密,这样对于线程任务的管理、线程资源的复用等功能几乎没有,或者只能自己手动实现,非常麻烦。
- JDK1.5的时候,出现了Executor线程池。线程池作为任务执行器,我们只需要创建指定的线程池,随后将线程任务传入线程池中,由线程池来确定是将任务直接分配给池中的线程去执行、还是创建线程并执行、或者是加入任务队列等待等等逻辑,使用线程池之后我们不再需要手动创建线程去执行,并且可以实现线程的复用以及线程任务的管理等强大的功能。执行器(线程池)将任务与线程解耦!
- 异步执行结果
- JDK1.5之前,在线程任务启动之后,对于线程任务监控几乎没有,我们不知道任务有没有完成,也没办法定义任务的返回值等一系列信息。
- JDK1.5的时候,出现了Future接口以及它的各种实现。这个接口体系代表了线程任务异步计算的结果,通常与Callable线程任务连用。利用了Future设计模式,在一个线程A执行线程任务的时候,我么可以在另一个线程B中异步的通过Future的实现的相关方法完成判断对应线程任务是否执行完毕、是否被取消、手动取消正在执行的线程任务、以及从执行完毕的线程任务中获取返回值等功能。
有了执行框架,我们只需创建线程任务、然后交给指定的线程池去执行,执行完毕之后等待获取返回结果即可,不再需要关注线程的创建、开启、执行、回收等基础性的中间工作,将任务与线程解耦,程序员更加的关注线程任务本身(这里是和业务相关的),有利于多线程程序的开发!
如果想要使用执行框架,只需要看相关api文档即可!那么我们有必要深入理解执行框架吗?当然时间充足的情况下是有必要的,只有我们知道了执行框架的原理之后,才能更好的使用它。执行框架属于JDK自带的基础框架,经历了时间和众多Java开发者的考验,不求能够手写,仅仅学习它的设计精华,包括各种设计模式,同时避开隐藏的坑,对于程序员个人的后续职业发展也是具有很大帮助的!
线程任务、执行器、执行结果这三部分,都可以围绕着Executor线程池展开,因此下面我们将从Executor入手,并且会穿插介绍Callable和Future的相关原理。
2 Executor线程池的概述
在上面关于Executor执行框架的概述中我们就说过,线程池作为连接线程任务和异步执行结果的执行器,其重要性不言而喻。
在Java框架设计中,一般涉及到资源相关的,并且资源具有创建消耗大、可复用的特点时,都采用了池化技术管理资源,形成一个“资源池”,池化技术可以带来以下一般性好处:对外部隐藏了资源的创建与释放的细节、实现资源的复用减少内存或者时间性能开销。常见Java中池化技术有:数据库连接池(管理数据库连接资源)、基本类型包装类中的缓存池(管理常用包装类对象)、Http连接池(管理http连接资源)、redis连接池(管理redis连接)等等,当然还包括马上要讲的线程池。
Executor线程池来自于JDK1.5的JUC包,使用线程池的目的或者好处如下:
- 实现线程资源的合理复用。线程资源属于操作系统核心资源之一,创建和销毁都需要占用系统资源和大量时间。使用线程池之后,不再需要开发者管理线程,线程的创建和销毁都交给线程池控制,实现线程的复用,减少线程资源的频繁的创建和销毁。
- 提升任务执行效率。当新来一个线程任务的时候,由于具有线程复用计数因此可以直接利用现有的线程去执行任务,不需要新建线程,这样一定程度上提升了执行效率。
- 可以对线程和线程任务实现实时监控和管理。比如目前活动线程数、曾经的最大线程数、已完成的任务数量等功能;比如控制最大线程数,在线程任务执行前-执行完毕后-线程池停止后具有可选的回调方法、移除某个线程任务、立即停止线程池等功能,他们都可以通过线程池的相关方法调用来实现。
JDK的线程池可扩展性极强,我们既可以利用定义好的线程池,也可以自定义线程池,很多其他框架组件也都是使用或者扩展了JDK线程池,比如ForkJoinPool分治框架(线程池框架的增强),guava的线程池MoreExecutors就是基于JDK线程池做的扩展,权限框架Shiro的PropertiesRealm属性文件认证类,JAVA RESTFUL请求服务框架Jersey,甚至单元测试框架junit等等框架都是用到了原生Executor线程池,下面来看看JDK的Executor线程池的主要原理吧!
3 Executor线程池的基本结构
从JDK1.5到JDK1.8,线程池的成员已经扩展的非常多了,下面仅仅列举了常用的关键的类和接口的uml关系:
下面先简单介绍核心接口以及实现类:
- Executor
- 作为线程池的顶级执行接口,也是一个函数式接口。只有一个execute方法,用于执行已提交的 Runnable 任务对象。
- 它不仅仅是一个接口,更是代表着一种将任务与每个任务将如何运行的机制(包括线程的创建、使用、调度等)分离开来的思想。使用者只需要提交任务,不需要创建线程,执行的细节被封装到Executor中,任务的执行方法可以根据实现者自由选择,可以实现为异步(使用新线程执行任务)、也可以是同步的(在调用者的线程中立即运行已提交的任务)。
- ExecutorService
- 继承并扩展了Executor接口的执行服务接口。
- 新增了可为跟踪一个或多个异步任务执行状况而生成 Future 的方法,比如submit方法,作为execute方法的扩展。新增了可以关闭线程池的方法,比如shutdown和shutdownNow方法。新增了批量执行任务的方法,比如 invokeAny 和 invokeAll方法。
- AbstractExecutorService
- 实现了ExecutorService的抽象类,提供 ExecutorService 执行方法的默认实现。比如对ExecutorService返回Future,实现为返回RunnableFuture。另一个作用是作为骨干实现最大限度地减少ExecutorService的实现类的代码。
- ThreadPoolExecutor
- 继承了ExecutorService的普通类,这是JDK线程池的核心实现。
- 它的构造器提供了各种可配置参数,比如线程数量、任务队列、拒绝策略等,方便我们自定义自己的线程池,以及各种钩子 (hook) 方法,方便追踪线程任务的执行,这是我们学习的重点,这里不做详细介绍。
- ScheduledThreadPoolExecutor
- 继承了ThreadPoolExecutor的普通类,可以看作功能的扩展或增强。
- 它能够将线程任务延迟指定时间后执行,或者间隔固定时间多次执行。功能与Timer类似,但ScheduledThreadPoolExecutor功能更强大、更灵活。Timer对应的是单个后台线程,而ScheduledThreadPoolExecutor可以在构造函数中指定多个对应的后台线程数。Timer中一个任务出现异常之后会影响其他任务的执行,但是ScheduledThreadPoolExecutor不会。Timer中一个任务耗时较常会影响其他任务的执行,ScheduledThreadPoolExecutor不会。
- Executors
- 独立出来的一个普通类(没有继承和实现关系,采用组合/聚合关系,图上没有注明),作为一个线程池工厂,提供各种实用方法。
- 提供了各种预定义线程池的实现,比如CachedThreadPool、FixedThreadPool等;提供了将Runnable包装、转换为Callable的方法;提供默认的ThreadFactory线程工厂的实现等功能。
下面我们主要学习ThreadPoolExecutor、ScheduledThreadPoolExecutor、Executors这三个类。另外JDK1.7的时候线程池新增了ForkJoinPool分治框架,这是对线程池的增强,后面的文章我们会讲解ForkJoinPool的源码!
4 ThreadPoolExecutor 线程池实现
4.1 ThreadPoolExecutor的概述
public class ThreadPoolExecutor
extends AbstractExecutorService
JDK线程池的关键实现,我们常用的Executors中的预定义线程池就有这个类的实例,当然也可以通过该类的构造器来创建一个自定义的线程池,提供任务执行,线程调度,线程池管理等等服务,理解了这个类,其他线程池的实现原理就很好理解了。
CallerRunsPolicy、AbortPolicy、DiscardPolicy、DiscardOldestPolicy是四个拒绝策略类。对于正在执行的线程数大于等于maxmumPoolSize以及workQueue容量已满时提交的任务,或者线程池正在关闭时的新提交的任务,线程池将会执行拒绝策略,任务会交给RejectedExecutionHandler来处理。
Worker是对线程池中工作线程的包装,用于对于传递的任务或者任务队列中的任务进行执行、中断控制等操作。这其中还涉及到AQS,即Worker实现了简单的不可重入锁。
4.2 ThreadPoolExecutor的主要属性
使用一个ctl原子变量来来同时记录线程池的运行状态(runState,简称rs)和线程池中线程数量(workerCount,简称wc)。int类型转换为二进制之后的最高三位保存线程池的状态,低29位保存线程数量。刚初始化ctl的时候,rs为RUNNING状态,wc为0。
另外还具有一个ReentrantLock独占锁,当改变线程池状态,比如添加工作线程、停止线程池,或者访问线程池共享参数信息比如当前线程数量的时候,因为这涉及到多个工作线程之间的共享信息比如线程池状态、工作线程数量等参数的同步,需要获取mainLock独占锁才能进行操作。
其他的属性将会在后面的构造器部分一一详解。
/**
* 用来同时记录线程池的运行状态(runState,简称rs)和线程池中线程数量(workerCount,简称wc)。ctl的值使用AtomicInteger原子类包装,能够保证数据是线程安全的。
* int类型转换为二进制之后的最高三位保存线程池的状态,低29位保存线程数量。刚初始化ctl的时候,rs为RUNNING状态,wc为0
* ReentrantReadWriteLock也是使用一个state变量保存写锁和读锁的获取信息,ConcurrentHashMap中的甚至使用一个lockState保存三种锁状态
*/
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
/**
* 线程数量掩码位数,int类型长度-3后的剩余位数,即wc所占位数为29
*/
private static final int COUNT_BITS = Integer.SIZE - 3;
/**
* 为了能正确保存线程数量,线程池的数量线程被限制为29位的最大值,即最大(2^29)-1个,而不是(2^31)-1个
*/
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
//ThreadPoolExecutor中定义了线程池的状态,存储在ctl的高三位中,一共有五种
/**
* RUNNING状态,11100000000000000000000000000000
*/
private static final int RUNNING = -1 << COUNT_BITS;
/**
* SHUTDOWN状态:00000000000000000000000000000000
*/
private static final int SHUTDOWN = 0 << COUNT_BITS;
/**
* STOP状态:00100000000000000000000000000000
*/
private static final int STOP = 1 << COUNT_BITS;
/**
* TIDYING状态:01000000000000000000000000000000
*/
private static final int TIDYING = 2 << COUNT_BITS;
/**
* TERMINATED状态:01100000000000000000000000000000
*/
private static final int TERMINATED = 3 << COUNT_BITS;
//通过对ctl的拆解、组合,获取相关的数据
/**
* 获取ctl的高3位,线程池运行状态
*
* @param c 此时的ctl值
* @return ctl的高3位的int值
*/
private static int runStateOf(int c)
//将c的低29位置为0并返回结果
return c & ~CAPACITY;
/**
* 获取ctl的低29位,线程数量
*
* @param c 此时的ctl值
* @return ctl的低29位的int值
*/
private static int workerCountOf(int c)
//将c的高3位置为0并返回结果
return c & CAPACITY;
/**
* 组合rs和wc,计算ctl新值
*
* @param rs 运行状态
* @param wc 线程数量
* @return 新ctl的int值
*/
private static int ctlOf(int rs, int wc)
//两者与运算
return rs | wc;
/**
* 线程任务队列,是一个阻塞队列
* 使用isEmpty来检测队列是否为空,而不是通过poll的返回值
*/
private final BlockingQueue<Runnable> workQueue;
/**
* 当改变线程池状态,比如添加工作线程、停止线程池,或者访问线程池共享参数信息比如当前线程数量的时候
* 因为这涉及到多个线程之间的共享信息比如线程池状态、工作线程数量等参数的同步,需要获取mainLock独占锁才行
*/
private final ReentrantLock mainLock = new ReentrantLock();
/**
* 包含全部工作线程的set集合
* 只有在持有mainLock锁的时候才能访问
*/
private final HashSet<Worker> workers = new HashSet<Worker>();
/**
* 主要是为了支持awaitTermination方法,外部线程调用awaitTermination方法之后
* 会判断线程池是否是TERMINATED状态,即终止状态,如果不是则调用线程在termination条件变量中等待,直到超时或者线程完毕
*/
private final Condition termination = mainLock.newCondition();
/**
* 记录到目前为止线程池中的拥有的最大线程数量
* 只有在持有mainLock锁的时候才能访问
*/
private int largestPoolSize;
/**
* 线程池已完成的任务数量,只有在某个Worker工作线程终止时才会更新
* 只有在持有mainLock锁的时候才能访问
*/
private long completedTaskCount;
/**
* ThreadPoolExecutor中的工作线程统一使用线程工厂来创建
* threadFactory用于保存传入的线程工厂实现,具有volatile的特性
* Executors中给出了默认实现,我们可以直接使用:Executors.defaultThreadFactory()
*/
private volatile ThreadFactory threadFactory;
/**
* 对于提交任务数超过maxmumPoolSize+workQueue之和时超出的任务,或者线程池正在关闭时的新提交的任务执行的拒绝策略,
* 任务会交给RejectedExecutionHandler的handler来处理,具有volatile的特性
* ThreadPoolExecutor中提供了默认实现:AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy
*/
private volatile RejectedExecutionHandler handler;
/**
* 空闲的工作线程的等待超时时间,具有volatile的特性
* 当存在的工作线程数量大于指定核心线程数量时,那么多余的线程会使用此超时时间,超过该时间没有工作则关闭线程
* 或者如果允许CoreThreadTimeOut,那核心线程也会使用此超时时间,超过该时间没有任务则关闭线程;否则,核心将永远等待新的任务。
*/
private volatile long keepAliveTime;
/**
* 是否允许核心线程应用空闲超时时间,具有volatile的特性
* 如果为false,那么即使核心线程空闲也会永远保持活动状态(不会被销毁)
* 如果为true,那么核心线程将会应用 keepAliveTime,在指定时间内等待工作,超时则被销毁(设置成功的要求是超时时间大于0)。
*/
private volatile boolean allowCoreThreadTimeOut;
/**
* 线程池核心线程数量,具有volatile的特性
* 除非设置了allowCoreThreadTimeOut=true,那么核心线程永远不会被销毁
*/
private volatile int corePoolSize;
/**
* 线程池最大线程数量,具有volatile的特性
* 不能超过(2^29)-1
*/
private volatile int maximumPoolSize;
//下面的属性涉及到Java安全模型:https://developer.ibm.com/zh/articles/j-lo-javasecurity/
//一般人接触不到
/**
* 用于校验是否具有shutdown 和 shutdownNow 关闭线程池的操作权限
*/
private static final RuntimePermission shutdownPerm =
new RuntimePermission("modifyThread");
/**
* 授权上下文环境对象
* 在GC标记ThreadPoolExecutor对象并调用finalize方法时调用,用于释放资源
*/
private final AccessControlContext acc;
4.2.1 ctl相关方法
围绕着ctl有一系列的简单的方法,它们被其他大量方法所调用,这里先介绍出来!
runStateOf(int c):获取ctl的高3位,即获取线程池运行状态值;
workerCountOf(int c):获取ctl的低29位,即获取线程数量值;
ctlOf(int rs, int wc):组合rs和wc,计算ctl新值;
runStateLessThan(int c, int s):c的运行状态值是否小于指定状态值s;
runStateAtLeast(int c, int s):c的运行状态值是否大于等于指定状态值s;
isRunning(int c):c的运行状态是否是RUNNING;
compareAndIncrementWorkerCount(int expect):尝试CAS的将ctl的WorkerCount线程数量部分自增1;
compareAndDecrementWorkerCount(int expect):尝试CAS的将ctl的WorkerCount线程数量部分自减1;
decrementWorkerCount():循环尝试CAS的将ctl的WorkerCount线程数量部分自减1,直到成功为止。只有在addWorkerFailed、processWorkerExit以及getTask方法中调用。
//通过对ctl的拆解、组合,获取相关的数据
/**
* 获取ctl的高3位,线程池运行状态
*
* @param c 此时的ctl值
* @return ctl的高3位的int值
*/
private static int runStateOf(int c)
//将c的低29位置为0并返回结果
return c & ~CAPACITY;
/**
* 获取ctl的低29位,线程数量
*
* @param c 此时的ctl值
* @return ctl的低29位的int值
*/
private static int workerCountOf(int c)
//将c的高3位置为0并返回结果
return c & CAPACITY;
/**
* 组合rs和wc,计算ctl新值
*
* @param rs 运行状态
* @param wc 线程数量
* @return 新ctl的int值
*/
private static int ctlOf(int rs, int wc)
//两者与运算
return rs | wc;
//不需要拆解ctl进行数据比较或者更改
/**
* 运行状态值是否小于指定状态值
*
* @param c 此时的ctl
* @param s 指定状态值
* @return true 是 false 否
*/
private static boolean runStateLessThan(int c, int s)
return c < s;
/**
* 运行状态值是否大于等于指定状态值
*
* @param c 此时的ctl
* @param s 指定状态值
* @return true 是 false 否
*/
private static boolean runStateAtLeast(int c, int s)
return c >= s;
/**
* 运行状态是否是RUNNING
*
* @param c 此时的ctl
* @return true 是 false 否
*/
private static boolean isRunning(int c)
return c < SHUTDOWN;
/**
* 尝试CAS的将ctl的WorkerCount线程数量部分自增1
*
* @param expect 预期的ctl值
* @return true 成功 false 失败
*/
private boolean compareAndIncrementWorkerCount(int expect)
return ctl.compareAndSet(expect, expect + 1);
/**
* 尝试CAS的将ctl的WorkerCount线程数量部分自减1
*
* @param expect 预期的ctl值
* @return true 成功 false 失败
*/
private boolean compareAndDecrementWorkerCount(int expect)
return ctl.compareAndSet(expect, expect - 1);
/**
* 循环尝试CAS的将ctl的WorkerCount线程数量部分自减1,直到成功为止
* 只有在addWorkerFailed、processWorkerExit以及getTask部分调用
*/
private void decrementWorkerCount()
do
while (!compareAndDecrementWorkerCount(ctl.get()));
4.2.2 线程池的状态
在上面的属性介绍中,我们知道线程池有5种状态:RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED。
RUNNING = -1 << COUNT_BITS,转换为二进制就是11100000000000000000000000000000
SHUTDOWN = 0 << COUNT_BITS,转换为二进制就是00000000000000000000000000000000
STOP = 1 << COUNT_BITS,转换为二进制就是00100000000000000000000000000000
TIDYING = 2 << COUNT_BITS,转换为二进制就是01000000000000000000000000000000
TERMINATED = 3 << COUNT_BITS,转换为二进制就是01100000000000000000000000000000
可以发现,运行状态的大小关系为:RUNNING < SHUTDOWN < STOP < TIDYING < TERMINATED,这在状态转换的时候非常有用,这样可以通过大小判断状态关系。
类似于线程的状态,线程池的状态也可以转换。但是又有不同,线程状态可以循环转换、相互转换,而一旦发生线程池的状态的转换,那么该转换不可逆。下面来看看线程池状态的转换规则:
详细说明:
状态名 | 说明 | 转换 |
RUNNING | 线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。 | 新建的线程池的初始状态就是RUNNING,并且线程池中的工作线程数量为0。 |
SHUTDOWN | 线程池处在SHUTDOWN状态时,不接收新任务,但内部正在执行的任务和队列里等待的任务,会执行完,随后会清理全部工作线程。 | RUNNING状态的线程池,调用shutdown方法,或者隐式调用了finalize方法(里面有shutdown方法时线程池状态将变成SHUTDOWN。 |
STOP | 线程池处在STOP状态时,不接收新任务,不处理已添加的任务(丢弃),并且会中断正在处理的任务,随后会清理全部工作线程。 | RUNNING or SHUTDOWN状态的线程池,调用shutdownNow方法,线程池状态将变成 STOP。 |
TIDYING | 所有的任务已执行完或被终止或被丢弃,ctl记录的workerCount工作线程数量为0,线程池会变为TIDYING状态。接着会执行钩子函数terminated()。 | SHUTDOWN状态的线程池,当任务队列为空并且线程池工作线程数workerCount为0时,线程池状态就会由 SHUTDOWN 自动转换为 TIDYING状态。 STOP状态的线程池,线程池中工作线程数workerCount为0时,线程池状态就会由STOP自动转换为TIDYING状态。 |
TERMINATED | 钩子函数terminated()执行完毕,就变成TERMINATED状态,线程池彻底终止。 | TIDYING状态的线程池,在接着执行完terminated()之后,线程池状态就会由TIDYING自动转换为 TERMINATED。 |
4.3 ThreadPoolExecutor的构造器
ThreadPoolExecutor的构造器是创建线程池的入口,JDK提供了四个构造函数。其中参数较少的的三个构造函数内部都是调用参数最多的那一个构造函数。
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue< Runnable > workQueue)
使用给定的初始参数和默认的线程工厂及默认的拒绝策略创建新的 ThreadPoolExecutor。
/**
* 使用给定的初始参数和默认的线程工厂及默认的拒绝策略创建新的 ThreadPoolExecutor。
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
//内部调用最多参数的构造器
//线程工厂传递的Executors的默认实现:Executors.defaultThreadFactory()
//拒绝策略传递的默认实现:defaultHandler
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue< Runnable > workQueue,ThreadFactory threadFactory)
使用给定的初始参数和默认的拒绝策略创建新的 ThreadPoolExecutor。
/**
* 使用给定的初始参数和默认的拒绝策略创建新的 ThreadPoolExecutor。
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
//内部调用最多参数的构造器
//拒绝策略传递的默认实现:defaultHandler
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue< Runnable > workQueue,RejectedExecutionHandler handler)
使用给定的初始参数和默认的线程工厂创建新的 ThreadPoolExecutor。
/**
* 使用给定的初始参数和默认的线程工厂创建新的 ThreadPoolExecutor。
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
//内部调用最多参数的构造器
//线程工厂传递的Executors的默认实现:Executors.defaultThreadFactory()
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue< Runnable > workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
最后一个构造器,使用给定的初始参数创建新的 ThreadPoolExecutor,一共有7个参数。
/**
1. 使用给定的初始参数创建新的 ThreadPoolExecutor。
2. 3. @param corePoolSize 核心线程数
3. @param maximumPoolSize 最大线程数
4. @param keepAliveTime 空闲线程等待超时时间
5. @param unit 超时时间单位
6. @param workQueue 阻塞任务队列
7. @param threadFactory 线程工厂
8. @param handler 拒绝策略
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
//一系列参数校验
/*
* 如果核心线程数小于0
* 或者 如果最大线程数小于等于0
* 或者 如果最大线程数小于核心线程数
* 或者 如果空闲线程等待超时时间小于0
*
* 满足上面一项,都将抛出IllegalArgumentException异常
*/
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
/*
* 如果阻塞任务队列为null
* 或者 如果线程工厂为null
* 或者 如果拒绝策略为null
*
* 满足上面一项,都将抛出NullPointerException异常
*/
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
//初始化用于安全管理器的上下文参数
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
//初始化核心线程数
this.corePoolSize = corePoolSize;
//初始化最大线程数
this.maximumPoolSize = maximumPoolSize;
//初始化阻塞任务队列
this.workQueue = workQueue;
以上是关于JUC—Executor线程池框架源码深度解析六万字的主要内容,如果未能解决你的问题,请参考以下文章
Java Executor源码解析—Executors线程池工厂以及四大内置线程池
通过Thread Pool Executor类解析线程池执行任务的核心流程
Java Executor源码解析—ThreadPoolExecutor线程池其他方法的源码