深入分析java线程池的实现原理
结合多篇文章整理,(仅用于面试)(相当于 copy,hhhh)
前言
线程是稀缺资源,如果被无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,合理的使用线程池对线程进行统一分配、调优和监控,有以下好处:
1、降低资源消耗;
2、提高响应速度;
3、提高线程的可管理性。
Java1.5中引入的Executor框架把任务的提交和执行进行解耦,只需要定义好任务,然后提交给线程池,而不用关心该任务是如何执行、被哪个线程执行,以及什么时候执行。
demo
public class ExecutorTest {
private static Executor executor = Executors.newFixedThreadPool(10);
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
executor.execute(new Task());
}
}
static class Task implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
}
1、Executors.newFixedThreadPool(10) 初始化一个包含 10 个线程的线程池 executor ;
2、通过 executor.execute() 方法提交 20 个任务,每个任务打印当前的线程名 。
3、负责执行任务的线程的生命周期都由 Executor 框架进行管理 ;
手写一个线程池
为了更好的理解和分析关于线程池的源码,我们先来按照线程池的思想,手写一个非常简单的线程池 。
其实很多时候一段功能代码的核心主逻辑可能没有多复杂,但是为了让核心流程顺利运行,就需要额外添加很多分支的辅助流程。
" 为了保护手才把卫生纸弄那么大 . "
实现流程

关于上图这个手写线程池的实现也非常简单,只回体现出核心流程,包括 :
- 有 n 个一直在运行的线程,相当于我们创建线程池允许的线程池大小 。
- 把线程提交给线程池运行 。
- 如果运行线程池已满,则把线程放入队列中 。
- 最后当有线程空闲时,则获取队列中的任务,让线程进行运行。
实现代码
public class ThreadPoolTrader implements Executor {
private final AtomicInteger ctl = new AtomicInteger(0);
private volatile int corePoolSize;
private volatile int maximumPoolSize;
private final BlockingQueue<Runnable> workQueue;
public ThreadPoolTrader(int corePoolSize, int maximumPoolSize, BlockingQueue<Runnable> workQueue) {
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
}
@Override
public void execute(Runnable command) {
int c = ctl.get();
if (c < corePoolSize) {
if (!addWorker(command)) {
reject();
}
return;
}
if (!workQueue.offer(command)) {
if (!addWorker(command)) {
reject();
}
}
}
private boolean addWorker(Runnable firstTask) {
if (ctl.get() >= maximumPoolSize) return false;
Worker worker = new Worker(firstTask);
worker.thread.start();
ctl.incrementAndGet();
return true;
}
private final class Worker implements Runnable {
final Thread thread;
Runnable firstTask;
public Worker(Runnable firstTask) {
this.thread = new Thread(this);
this.firstTask = firstTask;
}
@Override
public void run() {
Runnable task = firstTask;
try {
while (task != null || (task = getTask()) != null) {
task.run();
if (ctl.get() > maximumPoolSize) {
break;
}
task = null;
}
} finally {
ctl.decrementAndGet();
}
}
private Runnable getTask() {
for (; ; ) {
try {
System.out.println("workQueue.size:" + workQueue.size());
return workQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private void reject() {
throw new RuntimeException("Error!ctl.count:" + ctl.get() + " workQueue.size:" + workQueue.size());
}
public static void main(String[] args) {
ThreadPoolTrader threadPoolTrader = new ThreadPoolTrader(2, 2, new ArrayBlockingQueue<Runnable>(10));
for (int i = 0; i < 10; i++) {
int finalI = i;
threadPoolTrader.execute(() -> {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务编号:" + finalI);
});
}
}
}
// 测试结果
任务编号:1
任务编号:0
workQueue.size:8
workQueue.size:8
任务编号:3
workQueue.size:6
任务编号:2
workQueue.size:5
任务编号:5
workQueue.size:4
任务编号:4
workQueue.size:3
任务编号:7
workQueue.size:2
任务编号:6
workQueue.size:1
任务编号:8
任务编号:9
workQueue.size:0
workQueue.size:0
以上 ,关于线程池的实现还是非常简单的。从测试结果上已经可以把最核心的池化思想体现出来了。主要功能逻辑包括 :
ctl,用于记录线程池中线程数量。corePoolSize、maximumPoolSize,用于限制线程池容量。workQueue,线程池队列,也就是那些还不能被及时运行的线程,会被装入到这个队列中。execute,用于提交线程,这个是通用的接口方法。在这个方法里主要实现的就是,当前提交的线程是加入到worker、队列还是放弃。addWorker,主要是类Worker的具体操作,创建并执行线程。这里还包括了getTask()方法,也就是从队列中不断的获取未被执行的线程。
那么以上呢,就是这个简单线程池实现的具体体现。但如果深思熟虑就会发现这里需要很多完善,比如:线程池状态呢,不可能一直奔跑呀!?、线程池的锁呢,不会有并发问题吗?、线程池拒绝后的策略呢?,这些问题都没有在主流程解决,也正因为没有这些流程,所以上面的代码才更容易理解。
接下来,我们就开始分析线程池的源码,与我们实现的简单线程池参考对比,会更加容易理解😄!
线程池讲解
线程池类关系图

以围绕核心类 ThreadPoolExecutor 的实现展开的类之间实现和继承关系,如上图线程池类关系图。
- 接口
Executor、ExecutorService,定义线程池的基本方法。尤其是execute(Runnable command)提交线程池方法。 - 抽象类
AbstractExecutorService,实现了基本通用的接口方法。 ThreadPoolExecutor,是整个线程池最核心的工具类方法,所有的其他类和接口,为围绕这个类来提供各自的功能。Worker,是任务类,也就是最终执行的线程的方法。RejectedExecutionHandler,是拒绝策略接口,有四个实现类;AbortPolicy(抛异常方式拒绝)、DiscardPolicy(直接丢弃)、DiscardOldestPolicy(丢弃存活时间最长的任务)、CallerRunsPolicy(谁提交谁执行)。Executors,是用于创建我们常用的不同策略的线程池,newFixedThreadPool、newCachedThreadPool、newScheduledThreadPool、newSingleThreadExecutor。
ThreadPoolExecutor 运行机制
ThreadPoolExecutor 是如何运行,如何同时维护线程和执行任务的呢?其运行机制如下图所示:

线程池在内部实际上构建了一个生产者消费者模型,将线程和任务两者解耦,并不直接关联,从而良好的缓冲任务,复用线程。线程池的运行主要分成两部分:任务管理、线程管理。任务管理部分充当生产者的角色,当任务提交后,线程池会判断该任务后续的流转:
- (1)直接申请线程执行该任务;
- (2)缓冲到队列中等待线程执行;
- (3)拒绝该任务。线程管理部分是消费者,它们被统一维护在线程池内,根据任务请求进行线程的分配,当线程执行完任务后则会继续获取新的任务去执行,最终当线程获取不到任务的时候,线程就会被回收。
Executors 是Java线程池的工厂类,通过它可以快速初始化一个符合业务需求的线程池。比如 Executors.newFixedThreadPool() 方法可以生成一个拥有固定线程数的线程池 。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
其本质是通过不同的参数从而初始化一个 ThreadPoolExecutor 对象,具体参数描述如下 :
关键属性
public class ThreadPoolExecutor extends AbstractExecutorService {
// 控制变量-存放状态和线程数
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 任务队列,必须是阻塞队列
private final BlockingQueue<Runnable> workQueue;
// 工作线程集合,存放线程池中所有的(活跃的)工作线程,只有在持有全局锁mainLock的前提下才能访问此集合
private final HashSet<Worker> workers = new HashSet<>();
// 全局锁
private final ReentrantLock mainLock = new ReentrantLock();
// awaitTermination方法使用的等待条件变量
private final Condition termination = mainLock.newCondition();
// 记录峰值线程数
private int largestPoolSize;
// 记录已经成功执行完毕的任务数
private long completedTaskCount;
// 线程工厂,用于创建新的线程实例
private volatile ThreadFactory threadFactory;
// 拒绝执行处理器,对应不同的拒绝策略
private volatile RejectedExecutionHandler handler;
// 空闲线程等待任务的时间周期,单位是纳秒
private volatile long keepAliveTime;
// 是否允许核心线程超时,如果为true则keepAliveTime对核心线程也生效
private volatile boolean allowCoreThreadTimeOut;
// 核心线程数
private volatile int corePoolSize;
// 线程池容量
private volatile int maximumPoolSize;
// 省略其他代码
}
// 下面看参数列表最长的构造函数:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
corePoolSize
线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于 corePoolSize;如果当前线程数等于 corePoolSize,继续提交的任务会被保存到阻塞队列中,等待执行;如果执行了线程池的 prestartAllCoreThreads() 方法,线程池会提前创建并启动所有核心线程 。
多余提一嘴 :
线程池的基本大小,即在没有任务需要执行的时候线程池的大小,并且只有在工作队列满了的情况下才会创建超出这个数量的线程。这里需要注意的是:在刚刚创建ThreadPoolExecutor的时候,线程并不会立即启动,而是要等到有任务提交时才会启动,除非调用了prestartCoreThread/prestartAllCoreThreads事先启动核心线程。再考虑到keepAliveTime和allowCoreThreadTimeOut超时参数的影响,所以没有任务需要执行的时候,线程池的大小不一定是corePoolSize。
maximumPoolSize
线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于 maximumPoolSize ;线程池中的当前线程数目不会超过该值。
多余提一嘴 :
这里值得一提的是 largestPoolSize 参数,该变量记录了线程池在整个生命周期中曾今出现的最大线程个数 。为什么说是曾经呢 ?因为线程池创建之后,可以通过调用 setMaximumPoolSize() 改变运行的最大线程的大小 。
keepAliveTime
线程空闲时的存活时间,即当前线程没有任务执行时,继续存活的时间;默认情况下,该参数只会在线程数大于 corePoolSize 时才有用 ;
unit
keepAliveTime的单位;
workQueue
它是一个BlockingQueue接口,仅用于存放Runaable对象.根据队列功能的分类,在ThreadPoolExecutor中可以使用两种队列
用来保存等待被执行的任务的阻塞队列,且任务必须实现 Runnable 接口,在 JDK 中提供了如下阻塞队列:
1、ArrayBlockingQueue :基于数组结构的有界阻塞队列,按 FIFO 排序任务 ;
2、LinkedBlockingQuene :基于链表结构的无界阻塞队列,按 FIFO 排序任务,吞吐量通常要高于 ArrayBlockingQueue ;与有界队列相比,除非系统资源耗尽,否则不会存在任务入队失败的情况,若任务创建和处理速度差异很大,无界队列会快速膨胀导致系统资源耗尽
3、SynchronousQuene :一个不存储的阻塞队列,每个插入操作必须等待另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于 LinkedBlockingQuene ;
4、priorityBlockingQuene :具有优先级的无界阻塞队列 ;优先任务队列使用PriorityBlockingQueue实现,PriorityBlockingQueue是一个特殊的无界队列,创建PriorityBlockingQueue时可以传入Comparator对任务进行优先级处理,PriorityBlockingQueue和无界队列可能会发生的问题一样,不过PriorityBlockingQueue能控制任务的优先级别。
threadFactory
创建线程的工厂,通过自定义的线程工厂可以给每个新建的线程设置一个具有识别度的线程名
static class DefaultThreadFactory implements ThreadFactory {
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
}
handler
线程池的饱和策略 (也可以叫拒绝策略),当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了四种策略 :
1、AbortPolicy :直接抛出异常,默认策略。
2、CallerRunsPolicy :用调用者所在的线程来执行任务;
3、DiscardOldestPolicy :丢弃阻塞队列中靠最前的任务,并执行当前任务。
4、DiscardPolicy :直接丢弃任务 ;
当然也可以根据应用场景实现 RejectedExecutionHandler 接口,自定义拒绝策略,如记录日志或持久化存储不能处理的任务 。
Exectors 初始化线程池使用介绍
Exectors 工厂类提供了线程池的初始化接口,提供了创建适用于各种场景线程池的工具方法(静态方法),主要有如下四种 :
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
初始化一个指定线程数(固定线程数量)的线程池,其中 corePoolSize == maximumPoolSize,默认使用 LinkedBlockingQueue 作为阻塞队列,
具体的线程数量由nThreads参数指定。最开始该线程池中的线程数为0,之后每提交一个任务就会创建一个线程,直到线程数等于指定的nThreads参数,此后线程数量将不再变化;
不过需要注意:当线程池中没有可执行的任务时,也不会释放线程资源 。
newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
1、初始化一个可以缓存线程的线程池,默认缓存 60s,线程池的线程数可达到 Integer.MAX_VALUE, 即2147483647,内部默认使用 SynchronousQueue 作为阻塞队列 ;
2、和 newFixedThreadPool() 创建的线程池不同的是,newCachedThreadPool() 在没有任务执行时,当线程的空闲时间超过 keepAliveTime ,会自动释放线程资源,当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销 。
所以,使用该方法创建线程池,一定要注意控制并发的任务数量,否则创建大量的线程可能导致严重的性能问题 。
newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
初始化的线程池中只有一个线程,如果该线程异常结束,会重新创建一个新的线程继续执行任务,唯一的线程可以保证所提交的任务顺序执行,内部使用 LinkedBlockingQueue 作为阻塞队列 。
newScheduledThreadPool
初始化的线程池可以在指定的时间内周期性的执行所提交的任务,在实际的业务场景中可以使用该线程池定期的同步数据 。创建固定线程数量的线程池,而且以延迟或定时的方式来执行任务
实现原理
除了 newScheduledThreadPool() 的内部实现特殊一点之外,其他几个线程池都是基于 ThreadPoolExecutor 类实现的。
线程池内部状态

public class ThreadPoolExecutor extends AbstractExecutorService {
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
// Packing and unpacking ctl
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
}
其中AtomicInteger 变量 ctl 的功能非常强大,利用低29位表示线程池中线程数,通过高3位表示线程池中的运行状态 :
1、RUNNING:-1 << COUNT_BITS,即高3位为111,该状态的线程池会接收新任务,并处理阻塞队列中的任务;
2、SHUTDOWN: 0 << COUNT_BITS,即高3位为000,该状态的线程池不会接收新任务,但会处理阻塞队列中的任务;
3、STOP : 1 << COUNT_BITS,即高3位为001,该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务;
4、TIDYING : 2 << COUNT_BITS,即高3位为010;当所有的任务已终止,ctl 记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated() 在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
5、TERMINATED: 3 << COUNT_BITS,即高3位为011;线程池彻底终止,就变成TERMINATED状态。
任务提交方式
线程池框架提供了两种方式提交任务,根据不同的业务需求选择不同的方式。
Executor.execute()

通过Executor.execute()方法提交的任务,必须实现Runnable接口,该方式提交的任务不能获取返回值,因此无法判断任务是否执行成功。 Executor接口
ExecutorService.submit()

通过 ExecutorService.submit() 方法提交的任务,可以获取任务执行完的返回值。
任务执行----execute()
当向线程池中提交一个任务,线程池会如何处理该任务?
任务调度是线程池的主要入口,当用户提交了一个任务,接下来这个任务将如何执行都是由这个阶段决定的。了解这部分就相当于了解了线程池的核心运行机制。
由上文的任务分配部分可知,任务的执行有两种可能:一种是任务直接由新创建的线程执行。另一种是线程从任务队列中获取任务然后执行,执行完任务的空闲线程会再次去从队列中申请任务再去执行。第一种情况仅出现在线程初始创建的时候,第二种是线程获取任务绝大多数的情况。
execute() 实现原理
三种简要流程可供读者参考。究其原理都是互通的。
简要流程参考1、 :
简要流程参考2、 :

简要流程参考3、 :

public class ThreadPoolExecutor extends AbstractExecutorService {
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 如果运行的线程数小于corePoolSize,尝试创建一个新线程(Worker),并执行它的第一个任务command。
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 如果task成功插入等待队列,我们仍需要进行双重校验是否可以成功添加一个线程
* (因为有的线程可能在我们上次检查以后已经死掉了)或者在我们进入这个方法后线程池已经关闭了。
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
* 如果等待队列已满,我们尝试新创建一个线程。如果创建失败,我们知道线程已关闭或者已饱和,因此我们拒绝该任务。
*/
int c = ctl.get();
/*
高三位表示状态,低29位表示任务数量。
工作线程小于线程核心数,创建新的线程
*/
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
/*
线程池处于运行状态并且可以将任务插入队列
第一次校验线程池处于运行状态.
*/
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 第二次校验, 防止在第一次校验通过后线程池关闭
if (! isRunning(recheck) && remove(command))
reject(command);
/*
如果线程数=0(线程都挂掉了,比如: corePoolSize=0),新建线程且未指定firstTask,仅仅去轮询 workQueue
*/
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
/*
线程队列满了,尝试创建新线程执行任务(task),创建失败后拒绝任务,
创建失败原因: 1、线程池关闭;2、线程数已经达到 maxPoolSize
*/
else if (!addWorker(command, false))
reject(command);
}
}
具体的执行流程如下 :
1、workerCountOf() 方法根据 ctl 的低29位,得到线程池的当前线程数,如果线程数小于 corePoolSize ,则执行 addWorker() 方法创建新的线程执行任务,否则执行步骤 (2)、
2、如果线程池处于 RUNNING 状态,且把提交的任务成功放入阻塞队列中 ,则执行步骤 (3)、,否则执行步骤 (4)、
3、再次检查线程池的状态,如果线程池没有 RUNNING,并且成功从阻塞队列中删除任务,则执行 reject() 方法处理任务;否则判断线程数量为0后添加新线程。
4、执行 addWorker() 方法创建新的线程执行任务,如果 addWorker() 执行失败,则执行 reject() 方法处理任务;
- reject() 方法是一个饱和(拒绝)策略逻辑
补充解释:
在阅读这部分源码的时候,可以参考我们自己实现的线程池。其实最终的目的都是一样的,就是这段被提交的线程,启动执行、加入队列、决策策略,这三种方式。
ctl.get(),取的是记录线程状态和线程个数的值,最终需要使用方法workerCountOf(),来获取当前线程数量。workerCountOf() 执行的是 c & CAPACITY 运算
addWorker() 实现原理
线程管理部分的分水岭 :
- addWorker() 方法调用之前都是任务管理(任务的创建、拒绝、添加任务队列等;任务线程池的状态监控等);
- addWorker() 方法调用之后属于线程的管理。(线程的执行和阻塞等)
分别有两种图示供读者参考,究其原理都是相通的。
其执行流程如下图1所示:

其执行流程如下图2所示:

参数说明
- firstTask :worker 线程的初始任务,可以为空 。
- core :
- true :将 corePoolSize 作为上线;
- false :将 maximumPoolSize 作为上线 。
public class ThreadPoolExecutor extends AbstractExecutorService {
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
/*
外层循环判断线程池的状态,死循环每次获取最新的快照
这里的循环只是为了校验线程池的状态和线程数量.
循环成功结束后会原子性的将线程数量 +1
*/
for (;;) {
// 在这里进行获取一次上下文操作机制.
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
/*
1、判断线程池状态是否 >= SHUTDOWN
2、线程池状态 >= SHUTDOWN的情况下,判断状态是不是 = SHUTDOWN,是stop状态停止接受任务
3、如果线程池不是stop状态 判断任务是否存在 因为在SHUTDOWN状态下,不接收任务,只可以传空对象进来进行对workQueue中的任务进行处理
4、如果线程池不是stop状态并且是补充线程,用来处理队列中的数据的;如果队列为空,不用处理了
- 有以上的情况,代表不能生成新工作线程,返回false
- 线程池如果已经stop,或者处在shutdown状态但是已经到达corePoolSize,那么就会直接返回false
- 除了线程池正在关闭(shutdown),队列里还有未处理的task的情况,其他都不能添加
*/
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
// 这里对应execute方法中的 addWorker(null, false);
firstTask == null &&
// 这里对应execute方法中的 addWorker(null, false);
! workQueue.isEmpty()))
// 对应了execute方法中的!addWorker(firstTask, false) 失败后执行拒绝策略
return false;
// 内层循环判断是否达到线程池容量上线,worker +1
for (;;) {
// worker 数量
int wc = workerCountOf(c);
// 判断线程池的核心线程数和非核心线程数是否到达对应的上限, worker大于Integer最大上限或到达边界上限
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
// 到达上限,返回false,不创建线程
return false;
// 满足条件 先cas操作修改线程数量+1
if (compareAndIncrementWorkerCount(c))
// 修改成功 跳出循环.
break retry;
// 如果修改失败 刷新一下状态快照
c = ctl.get(); // Re-read ctl
// 判断是不是线程池被关闭了 如果没有关闭 内循环再循环一遍 去cas修改线程数量+1
if (runStateOf(c) != rs)
// 如果线程池状态发生变化,那么从外循环开始重来,重新交验
continue retry;
// else CAS failed due to workerCount change; retry inner loop
// CAS失败workerCount被其他线程改变,重新尝试内层循环CAS对workerCount+1
}
}
// 线程启动成功的标识
boolean workerStarted = false;
// 线程添加成功的标识
boolean workerAdded = false;
Worker w = null;
try {
/*
创建一个工作线程包装对象 worker
Worker构造器内部会将 state置为 -1,Worker继承了AbstractQueuedSynchronizer.
设置firstTask属性.
Worker实现了Runable接口,将this作为入参创建线程.
*/
w = new Worker(firstTask);
// 获得工作线程包装对象 worker 的实际工作线程.
final Thread t = w.thread;
/*
这里我为什么要判断为空呢? 是因为 worker 里面创建线程使用的是线程工厂.
而线程工厂是可以自定义的,如果你自定义的线程工厂出错了,没有创建成功,
这里就可以在下面 finally 中将上面 +1 的线程数 再-1
*/
if (t != null) {
// 使用 ReentrantLock 锁住这里,创建线程的过程不允许被其他线程打断.
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
// 获取最新的状态快照.
int rs = runStateOf(ctl.get());
// 判断线程池是否在执行中,如果在执行中,则进入if判断内部
if (rs < SHUTDOWN ||
/*
如果线程池不在执行中,判断一下线程池状态是不是等于 SHUTDOWN ,并且任务是不是为空
在 SHUTDOWN 情况下,是需要补充没有首个任务的补充线程来去到队列里面执行任务的,所以需要判断是不是补充线程
*/
(rs == SHUTDOWN && firstTask == null)) {
// 满足可以创建的条件之后,检查一下线程工厂刚刚创建的的这个线程有没有被启动过
// 如果已经被启动了,那么就要抛异常,因为线程必须走线程池下面的 start() 进行启动
if (t.isAlive()) // precheck that t is startable
// 如果已经被启动了,那么就要抛异常
throw new IllegalThreadStateException();
// 以上校验全部通过,将工作线程包装对象 worker 加入到工作线程集合中.
workers.add(w);
// 获取工作线程的总个数
int s = workers.size();
/*
如果工作线程的总个数大于当前线程总和.
largestPoolSize 这个变量是用来记录线程池曾经出现过的最大工作线程数量的
*/
if (s > largestPoolSize)
// 最大工作线程数修改为现在的新值.
largestPoolSize = s;
// 将 workerAdded 标识修改为 true, 表示 线程添加成功.
workerAdded = true;
}
} finally {
// 解锁
mainLock.unlock();
}
// 如果 线程添加成功.
if (workerAdded) {
// 启动线程
t.start();
// 将 workerStarted 标识修改为true 代表线程启动成功
workerStarted = true;
}
}
} finally {
/*
如果线程启动失败就回滚
这里不用 workerAdded 判断的原因是有可能线程添加成功了
但是到启动的时候,这个线程被其他地方先启动了,导致启动失败,所以使用 workerStarted 进行判断
*/
if (! workerStarted)
// 内部会使用 workers 集合 remove 这个 w 线程
// 然后将工作线程数量 cas 操作 -1
addWorkerFailed(w);
}
return workerStarted;
}
}
可以理解就是: 每一个 Worker 对象都是一个 AQS队列.
额外补充
添加执行任务的流程可以分为两块看,上面代码部分(for 循环) 是用于记录线程数量、下面代码部分是在独占锁里创建执行线程并启动。这部分代码在不看锁、CAS等操作,那么就和我们最开始手写的线程池基本一样了
if (rs >= SHUTDOWN &&! (rs == SHUTDOWN &&firstTask == null &&! workQueue.isEmpty())),判断当前线程池状态,是否为SHUTDOWN、STOP、TIDYING、TERMINATED中的一个。并且当前状态为SHUTDOWN、且传入的任务为 null,同时队列不为空。那么就返回 false。compareAndIncrementWorkerCount(c),CAS 操作,增加线程数量,成功就会跳出标记的循环体。runStateOf(c) != rs,最后是线程池状态判断,决定是否循环。- 在线程池数量记录成功后,则需要进入加锁环节,创建执行线程,并记录状态。在最后如果判断没有启动成功,则需要执行 addWorkerFailed 方法,剔除掉线程方法等操作。
addWorker方法有4种传参的方式:
- addWorker(command, true)
- 线程数小于 corePoolSize。判断 workers(HashSet<Worker) 大小,如果 worker 数量 >= corePoolSize 返回 false,否则创建 workers ,并执行 worker 的 run() 方法 (执行 firstTask 并轮询 workQueue);
- addWorker(command, false)
- 线程数大于 corePoolSize且 workQueue已满。如果 workQueue 数量 >= maximumPoolSize 返回 false,否则创建 worker 添加到 workers,并执行 worker 的 run() 方法 (执行 firstTask 并轮询 workQueue);
- addWorker(null, false)
- 没有 worker 存活,也就是 任务数量 runcount 为0,创建 worker 去轮询 workQueue,长度限制 maximumPoolSize
- addWorker(null, true)
- 在
execute()方法中只使用了前三种,结合这个核心方法进行以下分析
- 在
以上无论哪种方式都需要进行相关的ReentrantLock的加锁,所以效率和性能不会特别好。所以有了一个小办法,prestartAllCoreThreads()
prestartAllCoreThreads() 这个方法调用,启动所有的核心线程去轮询 workQueue。因为 addWork() 是需要上锁的,预启动核心线程可以提高执行效率 .
工作线程内部类Worker 原理分析
线程管理的核心类
Worker执行任务的模型如下图所示:

addWorker() 方法构造了一个 Worker对象,并且把 firstTask 封装到 Worker 对象中,那么它是做什么的呢 ?
/**
* Class Worker mainly maintains interrupt control state for
* threads running tasks, along with other minor bookkeeping.
* This class opportunistically extends AbstractQueuedSynchronizer
* to simplify acquiring and releasing a lock surrounding each
* task execution. This protects against interrupts that are
* intended to wake up a worker thread waiting for a task from
* instead interrupting a task being run. We implement a simple
* non-reentrant mutual exclusion lock rather than use
* ReentrantLock because we do not want worker tasks to be able to
* reacquire the lock when they invoke pool control methods like
* setCorePoolSize. Additionally, to suppress interrupts until
* the thread actually starts running tasks, we initialize lock
* state to a negative value, and clear it upon start (in
* runWorker).
* 1、Worker类主要负责运行线程状态的控制.
* 2、Worker继承了AQS从而实现了简单的获取锁和释放锁的操作.用于避免中断等待执行任务的线程同时也中断正在运行中的线程(线程刚启动,还没开始执行任务.)
* 3、自己实现不可重入锁,是为了避免在执行线程池状态控制的方法的时候中断正在开始运行的线程.
* - 例如: setCorePoolSize 的时候中断正在开始运行的线程.
* - 调用 setCorePoolSize() 方法可能会调用 interruptIdleWorkers() 方法,该方法中会调用 worker 的 tryLock()方法中断线程.
* - 自己实现锁可以确保工作线程启动之前不会被中断.
*/
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L;
/** Thread this worker is running in. Null if factory fails. */
// 封装任务线程机制.真正执行 task 的线程,从构造函数可知是由ThreadFactury创建的
final Thread thread;
/** Initial task to run. Possibly null. */
// 需要执行的 task
Runnable firstTask;
/** Per-thread task counter */
// 完成的任务书数,用于线程池的统计
volatile long completedTasks;
/**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
*/
Worker(Runnable firstTask) {
// 状态置为-1,如果中断线程需要CAS将state 从0->1,以此来保证只能中断从 workerQueue getTask 的线程
// 防止在调用 runWorker(),也就是真正执行 task 前中断 thread
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker */
// 核心方法
public void run() {
// 该方法首先执行 w.unlock(), 就是把state置为 0,对该线程的中断就可以进行操作了.
runWorker(this);
}
// Lock methods
//
// The value 0 represents the unlocked state.
// The value 1 represents the locked state.
protected boolean isHeldExclusively() {
return getState() != 0;
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// 在 setCorePoolSize()/shutdown() 等方法中断 worker 线程时需要调用该方法.
// 确保中断的是从 workerQueue getTask 的线程.
// 调用 tryRelease() 修改为 state=0, LockSupport.unpark(thread) 恢复下一个等待锁的线程.
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
总结 Worker 内部类的作用
1、每一个 Worker 都是一条线程,同时里面包含了一个 firstTask,即初始化时要被首先执行的任务.
2、最终执行的是 runWorker() 方法 ;
3、Worker 类继承了 AQS (AbstractQueuedSynchronizer) 并实现了 Runnable 接口,注意其中的 firstTask 和 thread 属性 ;
- firstTask :用来保存传入要执行的任务;
- thread :调用构造方法时通过 ThreadFactory 来创建的线程,是用来处理任务的线程.
4、在调用构造方法时,需要传入要执行的任务。这里通过 getThreadFactory().newThread(this); 来创建一个线程,newThread() 方法传入的参数是 this,因为 Worker 本身继承了 Runnable 接口。也就是一个线程,所以一个 Worker 对象在启动的时候会调用 Worker 类中 的 run() 方法.
5、Worker 自己继承了 AQS,使用 AQS 来实现独占锁的功能。使用 tryAcquire() 方法是不允许重入的,而 ReentrantLock 是允许重入的.
- 多余补充 :广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁。ReentrantLock 和 synchronized 都是可重入锁,
6、lock() 方法一旦获取独占锁,表示当前线程正在执行任务,会有以下几个作用 ;
- 1、如果正在执行任务,则不应该被中断线程;
- 2、如果该线程现在不是独占锁的状态 ,也就是空闲的状态,说明它没有在处理任务,这时可以对该线程进行中断;
- 3、线程池在执行 setCorePoolSize()、shutdown()方法或 tryTerminate() 方法时会调用 interruptldleWorkers() 方法来中断空闲的线程,而 interruptldleWorkers() 方法会使用tryLock方法去判断线程池中的线程是否是空闲状态;
- 4、之所以设置不可重入,是不希望任务在调用像 setCorePoolSize 这样的线程池控制方法时重新获取锁,会中断正在运行的线程 ;
7、这个 firstTask 任务可以有也可以为null。如果这个值是非空的,那么线程就会在启动初期立即执行这个任务,也就对应核心线程创建时的情况;如果这个值是null,那么就需要创建一个线程去执行任务列表(workQueue)中的任务,也就是非核心线程的创建。
runWorker 原理分析
执行流程如下图所示:

final void runWorker(Worker w) {
// 获取当前线程
Thread wt = Thread.currentThread();
// 获取 worker 的第一个任务对象引用,赋值给 task,即 task 指向worker的第一个任务对象.
Runnable task = w.firstTask;
// 上面拿到了快照,在这里将成员指针置为null,避免一直引用导致无法 GC 回收.
// 同时也确保了后续重新进入 runWorker() 方法的时候,不会重复执行第一个任务.
w.firstTask = null;
// worker 解锁,表示线程此时可以被打断了,对应构造方法中的setState(-1);
// 这里将 state+1 变为 0.
w.unlock(); // allow interrupts
// 异常标识位.
boolean completedAbruptly = true;
try {
// 这个 while 循环在这里实现了 [线程复用]
// 判断第一个任务是否不为空,不为空就直接进去执行任务了.
// 如果第一个任务为空,那么去getTask()方法中获取任务.
while (task != null || (task = getTask()) != null) {
// 加锁,表示线程池此时不可被打断.
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
// 判断线程池状态是否 >= STOP,如果大于等于,那么将线程的中断标识位置为true,表示此时任务线程会被打断
if ((runStateAtLeast(ctl.get(), STOP) ||
// 如果线程池状态小于 STOP,那么调用Thread.interrupted()判断是否线程已经被设置了中断标识位,同时清除了中断标识位(即重新设置为false)
(Thread.interrupted() &&
// 如果线程池小于 STOP,并且线程已经被发送了中断标识位,就需要再判断线程池是不是进入了 STOP状态.
// 因为只有 STOP状态设置的中断位需要处理,并由线程池处理,外部对线程设置的中断标识位在这里会被清除掉.
runStateAtLeast(ctl.get(), STOP))) &&
// 上面为true之后,标识线程需要被中断,判断以下中断标识位是否存在,不存在就true,进去if逻辑给该线程设置中断,存在就不用设置了,交给下面的run()方法去处理.
!wt.isInterrupted())
/*
最终结果如下:
- 如果线程池达到了 STOP 状态,那么设置中断标识位交给线程由任务中的 run() 去处理
- 如果线程没有达到 STOP 状态,但是被外部设置了中断标识位,使用isInterrupted() 清空这个标识位.
简单来说,就是如果线程池正在关闭,确保线程是被中断了的;如果线程池没关闭,确保线程是没被中断的
*/
wt.interrupt();
try {
// beforeExecute()是空方法,由子类具体实现
// 前置切面方法, 扩展线程池就是在这里面去写一些线程执行前需要处理的逻辑.
beforeExecute(wt, task);
Throwable thrown = null;
try {
// 执行任务
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
// afterExecute()空方法,由子类具体实现
// 后置切面方法, 拓展线程池就是在这里去写线程执行后需要处理的逻辑 ,即使出现Throwable异常,放入异常对象 执行后置切面
afterExecute(task, thrown);
}
} finally {
// 第一个任务执行完成后修改引用置空,这里是死循环,方便GC回收,不然回收不了.
// 同时也避免重复执行任务
task = null;
// 任务执行数量 +1
w.completedTasks++;
// worker 解锁
w.unlock();
}
}
/*
如果死循环结束, 要么worker在getTask()方法中被打断返回了false,要么就是发生了异常
如果是worker在getTask()中被打断了返回了false,那么会走到这里,将异常标识位置为false.
有异常,存在抛异常且没有处理的地方只有两个地方.
beforeExecute()和afterExecute()
代码中内层的try之后又重新将异常抛了出来.
外层的try之后没有catch() 所以最好不会走到这里,而是直接结束.
最终导致 completedAbruptly 是初始值结果 true.
*/
completedAbruptly = false;
} finally {
// 获取不到任务时,主动回收自己
processWorkerExit(w, completedAbruptly);
}
}
其实,有了手写线程池的基础,到这也就基本了解了,线程池在干嘛。到这里最核心的点就是 task.run() 让线程跑起来。额外再附带以下其他流程 :
beforeExecute、afterExecute,线程执行的前后做一些需要处理的逻辑。可以根据业务场景自定义 beforeExecute() 和 afterExecute() 方法;- 另外这里的锁操作是 Worker 继承 AQS 自己实现的不可重入的独占锁。
processWorkerExit,如果你感兴趣,类似这样的方法也可以深入了解下。在线程退出时候workers做到一些移除处理以及完成任务数等,也非常有意思
最终执行了我们提交的Runnable任务。在runWorker()方法中通过一个while循环来让Worker对象一直来执行任务。当传入的task对象不为空或者通过getTask()方法能从任务队列中获取到任务时,worker就会一直执行。否则将在finally语句块中调用processWorkerExit退出,让线程中断,最终销毁。
getTask()方法是一个阻塞的方法,当能从任务队列中获取到任务时,就会立即返回一个任务。如果获取不到任务,就会阻塞 (阻塞并挂起,不会占用cpu资源)。它支持超时,当超过线程池初始化时指定的线程最大存活时间后,就会返回null,从而导致worker线程退出while循环,最终线程销毁。
getTask() 队列获取任务
任务的执行有两种可能:一种是任务直接由新创建的线程执行。另一种是线程从任务队列中获取任务然后执行,执行完任务的空闲线程会再次去从队列中申请任务再去执行。第一种情况仅出现在线程初始创建的时候,第二种是线程获取任务绝大多数的情况。
线程需要从任务缓存模块中不断地取任务执行,帮助线程从阻塞队列中获取任务,实现线程管理模块和任务管理模块之间的通信。这部分策略由getTask方法实现,其执行流程如下图所示:

private Runnable getTask() {
// 当前线程是否已经超时, 默认false
boolean timedOut = false; // Did the last poll() time out?
// 死循环从任务队列中获取任务.
for (;;) {
// 获取线程池状态快照.
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
/*
判断线程池状态是否大于 SHUTDOWN
状态 >= SHUTDOWN情况下,再判断是不是 >= STOP 或者任务队列为空
任务队列为空也就是没有剩余任务要执行了,线程结束
*/
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
// 工作线程数量 -1
decrementWorkerCount();
// 返回 null,让外部 runWorker 死循环跳出,结束线程
return null;
}
// 跑到这里说明线程池还处于RUNNING状态,通过上面校验后,获取工作线程数量.
int wc = workerCountOf(c);
// Are workers subject to culling?
/*
判断核心线程是否需要超时或者当前工作线程数量是否大于核心线程数
allowCoreThreadTimeOut 是通过线程池的同名方法设置的.
1、allowCoreThreadTimeOut默认值为false,如果设置为true,则允许核心线程也能通过poll()方法从任务队列中拉取任务
2、工作线程数大于核心线程数的时候,说明线程池中创建了额外的非核心线程,这些非核心线程一定是通过poll()方法从任务队列中拉取任务
timed 就是当前线程是否可以超时标识.
*/
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
/*
如果工作线程数量大于最大线程数(说明了通过setMaximumPoolSize()方法减少了线程池容量) ⬅️
或者 (当前线程可以超时并且当前线程已经超时) ⬅️ 主要重点在这里
*/
if ((wc > maximumPoolSize || (timed && timedOut))
/*
上面都满足的情况下,保证当前线程数量大于1,或者任务队列里面已经没有任务了.
wc(工作线程数量) > 1的情况是用来避免结束当前线程后,一个线程都没有无法执行队列中的任务了.
*/
&& (wc > 1 || workQueue.isEmpty())) {
// 上面的条件都满足, cas工作线程数量 -1
if (compareAndDecrementWorkerCount(c))
// 返回null 让外部runWorker死循环跳出 结束线程
return null;
// cas失败代表有别的线程被加进来或者被干掉了 continue重新走一遍校验
continue;
}
// 不满足上面的校验.开始获取任务.
try {
// 通过 timed 是否可以超时标记决定使用 poll() 或者 take(), poll()会超时,take()不会
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
/*
只有非null时候才返回,null的情况下会进入下一轮循环
如果可以超时 超时后任务为空 就把超时标示设置为true已超时
在下一次循环的时候 如果满足(timed && timedOut) 就会结束当前这个已经超时的任务
*/
if (r != null)
// 正常获取到任务 继续执行
return r;
// 跑到这里说明上一次从任务队列中获取到的任务为null,一般是workQueue.poll()方法超时返回null
// 把超时标示设置为true已超时
timedOut = true;
} catch (InterruptedException retry) {
// 如果线程被中断 此时是不允许中断的 try住异常 然后判定为未超时 重新获取
timedOut = false;
}
}
}
整个 getTask() 操作再自旋下完成 :
- getTask() 方法从阻塞队列中获取等待被执行的任务,也就是一条条拿线程方法
timed,是allowCoreThreadTimeOut得来的。最终timed为 true 时,则通过阻塞队列的poll方法进行超时控制。- 如果在
keepAliveTime时间内没有获取到任务,则返回null。如果为false,则阻塞。 - 如果timed为true,通过poll()方法做超时拉取,keepAliveTime时间内没有等待到有效的任务,则返回 null
- 如果timed为false,通过take()做阻塞拉取,会阻塞到有下一个有效的任务时候再返回(一般不会是null )
- 如果在
1、workQueue.take:如果阻塞队列为空,当前线程会被挂起等待;当队列中有任务加入时,线程被唤醒,take方法返回任务,并执行;
2、workQueue.poll:如果在keepAliveTime时间内,阻塞队列还是没有任务,则返回null;
补充说明 : 这段逻辑大多数情况下是在针对非核心线程。
在execute()方法中,当线程池总数已经超过了corePoolSize并且还小于maximumPoolSize时,当任务队列已经满了的时候,会通过addWorker(task,false)添加非核心线程。而这里的逻辑恰好类似于addWorker(task,false)的反向操作,用于减少非核心线程,使得工作线程总数趋向于corePoolSize。
如果对于非核心线程,上一轮循环获取任务对象为null,这一轮循环很容易满足timed && timedOut为true,这个时候getTask()返回null会导致Worker#runWorker()方法跳出死循环,之后执行processWorkerExit()方法处理后续工作,而该非核心线程对应的Worker则变成 “游离对象” ,等待被JVM回收。
当allowCoreThreadTimeOut设置为true的时候,这里分析的非核心线程的生命周期终结逻辑同时会适用于核心线程。那么可以总结出keepAliveTime的意义:
- 当允许核心线程超时,也就是
allowCoreThreadTimeOut设置为true的时候,此时keepAliveTime表示空闲的工作线程的存活周期。 - 默认情况下不允许核心线程超时,此时
keepAliveTime表示空闲的非核心线程的存活周期。
在一些特定的场景下,配置合理的keepAliveTime能够更好地利用线程池的工作线程资源。
processWorkerExit() 源码探秘
线程池回收过程 
线程池需要管理线程的生命周期,需要在线程长时间不运行的时候进行回收。线程池使用一张Hash表去持有线程的引用,这样可以通过添加引用、移除引用这样的操作来控制线程的生命周期。这个时候重要的就是如何判断线程是否在运行。
processWorkerExit()方法是为将要终结的Worker做一次清理和数据记录工作(因为processWorkerExit()方法也包裹在runWorker()方法finally代码块中,其实工作线程在执行完processWorkerExit()方法才算真正的终结)。 ( 需要注意:获取不到任务时,主动回收自己 )
事实上,在这个方法中,将线程引用移出线程池就已经结束了线程销毁的部分。但由于引起线程销毁的可能性有很多,线程池还要判断是什么引发了这次销毁,是否要改变线程池的现阶段状态,是否要根据新状态,重新分配线程。

private void processWorkerExit(Worker w, boolean completedAbruptly) {
/*
这个字段追溯到runWorker() 方法中的异常标示
如果runWorker() 是正常通过getTask()退出 此时为false
如果是在自定义的切面前后方法中抛异常了 此时为true
*/
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
// 工作线程数量-1
decrementWorkerCount();
// 使用ReentrantLock进行原子行操作
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 将工作线程执行完成的任务数量累加给线程池
completedTaskCount += w.completedTasks;
// 从工作线程集合中移除当前工作线程
workers.remove(w);
} finally {
mainLock.unlock();
}
// 见下一小节分析,用于根据当前线程池的状态判断是否需要进行线程池terminate处理
tryTerminate();
// 获取当前线程池状态快照.
int c = ctl.get();
// 判断当前线程是不是小于STOP(也就是处于RUNNING或者SHUTDOWN状态的前提下), STOP就不执行任务了 剩下的状态还要执行任务
// 1.如果线程不是由于抛出用户异常终结,如果允许核心线程超时,则保持线程池中至少存在一个工作线程
// 2.如果线程由于抛出用户异常终结,或者当前工作线程数,那么直接添加一个新的非核心线程
if (runStateLessThan(c, STOP)) {
// 判断是不是正常退出 如果不是正常退出 就直接补充一个线程去执行任务
if (!completedAbruptly) {
// 是正常退出的情况下 通过核心线程是否和超时, 获得线程池最小线程数量
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
// 如果线程池最少数量是0, 并且线程池任务队列还有任务没走完
if (min == 0 && ! workQueue.isEmpty())
// 那么就必须最少要有一个线程去执行队列里的任务 所以设置为1
min = 1;
/*
判断线程池线程数量是否大于等于最小数量
如果大于最小数量 那么当前线程return出去后 就回到了runWorker()的最后一句话,当前线程结束
直接返回不新增非核心线程
*/
if (workerCountOf(c) >= min)
// 当前线程结束 (等待回收)
return; // replacement not needed
}
// 补充一个线程去执行任务 同时当前线程结束(等待回收)
addWorker(null, false);
}
}
上面代码 31 行后面部分区域,会判断线程池的状态,如果线程池是RUNNING或者SHUTDOWN状态的前提下,如果当前的工作线程由于抛出用户异常被终结,那么会新创建一个非核心线程。如果当前的工作线程并不是抛出用户异常被终结(正常情况下的终结),那么会这样处理:
allowCoreThreadTimeOut为true,也就是允许核心线程超时的前提下,如果任务队列空,则会通过创建一个非核心线程保持线程池中至少有一个工作线程。allowCoreThreadTimeOut为false,如果工作线程总数大于corePoolSize则直接返回,否则创建一个非核心线程,也就是会保持线程池中的工作线程数量趋向于corePoolSize。
processWorkerExit()执行完毕之后,意味着该工作线程的生命周期已经完结。
事实上,到这里线程池的 execute() 方法就算分析完了 。
tryTerminate() 源码分析
每个工作线程终结的时候都会调用 tryTerminate() 方法 :
final void tryTerminate() {
// 看到for(;;)就知道里面有cas操作
for (;;) {
// 获取当前线程池状态快照.
int c = ctl.get();
/*
判断线程池的状态,如果是下面三种情况的任意一种情况则直接返回.
1、线程池处于 RUNNING 状态.
2、线程池至少为 TIDYING 状态,也就是TIDYING或者TERMINATED状态,意味着已经有线程过了判断到了下面的cas地方去了 当前线程可以直接结束了.
3、线程池状态=SHUTDOWN, 并且任务队列不为空. 这里是线程刚刚调用了shutdown()或者shutdownNow()的时候判断的
- 如果线程池小于STOP就代表线程池工作线程还没有整理好,需要整理
- 所以再判断workQueue是不是为空 如果不为空就return了 如果为空再走下面判断
*/
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
/*
如果能走到这里,说明线程池已经开始结束了,要整理所有内容.
判断工作线程的数量是不是0
- 如果没有工作线程了那么当前线程就是工作线程的最后一个线程了 走下面的代码
- 如果还有线程 那么通过ONLY_ONE打断一个线程 由下一个线程再来判断
最终保证最后一个线程到这里的时候 走下面的整理代码
*/
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
}
// 上面的判断保证线程池只有最后一个线程的时候才走到这里,只有最后一个线程执行到这里的代码.
final ReentrantLock mainLock = this.mainLock;
// 加锁 防止同时两个线程同时退出导致通过了workerCountOf(c) != 0的校验
mainLock.lock();
try {
/*
cas 将线程池状态修改为 TIDYING,整理完成状态
上面两个线程可能的并发情况
- 第一个线程获取锁进来,第二个等待锁,等到整个线程处理成功,释放锁的时候
第二个线程获取到锁进来的时候,由于值传递 c 还是第一个线程修改前的状态,会修改失败,自动重试.
然后在上面的判断 runStateAtLeast(c, TIDYING) 时直接结束了.
*/
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
// 钩子函数,用于子类实现一些要关闭的时候需要处理的代码
terminated();
} finally {
// 执行完成后将线程池修改为 TERMINATED 终结状态.
ctl.set(ctlOf(TERMINATED, 0));
// 唤醒等待线程.
// 也就是线程池调用shutdown()之后调用的awaitTermination()阻塞等待线程池结束的那个方法
// 唤醒阻塞在termination条件的所有线程,这个变量的await()方法在awaitTermination()中调用
termination.signalAll();
}
// 最终线程退出 线程池终结
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
// 解析interruptIdleWorkers()
// 中断空闲的工作线程, onlyOne为true的时候,只回中断工作线程集合中的某一个线程.
private void interruptIdleWorkers(boolean onlyOne) {
// 加锁.
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 遍历整个线程集合
for (Worker w : workers) {
// 获取真实线程
Thread t = w.thread;
// 这里判断线程不是中断状态并且尝试获取锁成功的时候才进行线程中断
/*
如果线程已经有了中断位,那么不用中断.
如果线程没有中断位,那么短路 调用 w.tryLock()尝试获取 worker 的锁
在 Worker 的构造方法中,将自身的state置为了-1, 表示线程都还没有运行,所以不允许被打断,会在runWorker中通过对线程池状态的判断进行结束
以及在 runWorker()方法中加锁,避免在线程执行过程中被打断,线程执行完会释放锁,在getTask()中进行打断
所以这里的 tryLock() 就是在判断线程是否没有执行, 或者说线程正在执行中.
*/
if (!t.isInterrupted() && w.tryLock()) {
try {
// 满足上面的条件, 进来设置中断位.
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
// 这里跳出循环,也就是只中断集合中第一个工作线程
// 是否只打断一个 shudown()会全部打断 而上面的tryTerminate()会只打断一个
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
这里有疑惑的地方是tryTerminate()方法的第二个if代码逻辑:工作线程数不为0,则中断工作线程集合中的一个空闲的工作线程。方法API注释中有这样一段话:
If otherwise eligible to terminate but workerCount is nonzero, interrupts an idle worker to ensure that shutdown signals propagate.
当满足终结线程池的条件但是工作线程数不为0,这个时候需要中断一个空闲的工作线程去确保线程池关闭的信号得以传播。
下面将会分析的 shutdown() 方法中会通过 interruptIdleWorkers() 方法中断所有的空闲线程,这个时候有可能存在非空闲的线程正在执行某个任务。任务执行完毕之后,如果它刚好是核心线程,就会在下一轮循环中阻塞在任务队列的 task() 方法,如果不做额外的干预,它甚至会在线程池关闭之后永久阻塞在任务队列的 task() 方法中。为了避免这种情况发生,每个工作线程退出的时候都会尝试中断工作线程集合中的某一个空闲的线程,确保所有空闲的线程都能够正常退出 。
interruptIdleWorkers() 方法中会对每一个工作线程先进行 tryLock() 判断,只有返回 true 才有可能进行线程中断。我们知道runWorker()方法中,工作线程在每次从任务队列中获取到非null的任务之后,会先进行加锁Worker#lock()操作,这样就能避免线程在执行任务的过程中被中断,保证被中断的一定是空闲的工作线程。
shutdown() 源码分析
ThreadPoolExecutor.java 线程池关闭的操作有几个相关的变体方法,先看 shutdown() :
public void shutdown() {
// 加锁,防止并发
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// jdk 权限校验线程权限.安全策略相关判断
checkShutdownAccess();
/*
cas修改线程状态为 SHUTDOWN
随后 addWorker() 方法中的判断就不会再接受新的线程创建了.
只会有补充线程去执行 workQueue 中的任务
*/
advanceRunState(SHUTDOWN);
// 将所有线程中断标识位置为true, 中断所有的空闲的工作线程, 上一小节有介绍.
interruptIdleWorkers();
// 钩子函数,留给子类去实现的.
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
// 调用 tryTerminate() 方法判断是否需要中断一个空闲线程.
tryTerminate();
}
接着看shutdownNow()方法:
public List<Runnable> shutdownNow() {
// 没有执行完的任务
List<Runnable> tasks;
// 上锁 不允许并发
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// jdk 权限校验线程权限.安全策略相关判断
checkShutdownAccess();
/*
cas操作会将线程修改为 STOP 状态.
随后 addWorker() 方法将不再接收新线程,也不会补充线程去执行任务.
所有判断状态的地方都会将任务线程任务打断退出.
*/
advanceRunState(STOP);
// 将所有线程中断标识位置为true, 中断所有的工作线程
interruptWorkers();
// 将队列中的所有任务全部 remove, 清空工作队列并且取出所有的未执行的任务
tasks = drainQueue();
} finally {
mainLock.unlock();
}
// 线程池最终退出的方法,在上一小节有介绍.
tryTerminate();
// 返回所有没有执行的任务
return tasks;
}
shutdownNow()方法会把线程池状态先更变为STOP,中断所有的工作线程(AbstractQueuedSynchronizer的state值大于0的Worker实例,也就是包括正在执行任务的Worker和空闲的Worker),然后遍历任务队列,取出(移除)所有任务存放在一个列表中返回。
最后看awaitTermination()方法:
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
// 转换timeout的单位为纳秒
long nanos = unit.toNanos(timeout);
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 自旋,等待直到线程池状态变更为 TERMINATED ,每次循环等待 nanos 纳秒.
for (;;) {
if (runStateAtLeast(ctl.get(), TERMINATED))
return true;
if (nanos <= 0)
return false;
nanos = termination.awaitNanos(nanos);
}
} finally {
mainLock.unlock();
}
}
awaitTermination()虽然不是shutdown()方法体系,但是它的处理逻辑就是确保调用此方法的线程会阻塞到tryTerminate()方法成功把线程池状态更新为TERMINATED后再返回,可以使用在某些需要感知线程池终结时刻的场景。
有一点值得关注的是:shutdown()方法只会中断空闲的工作线程,如果工作线程正在执行任务对象Runnable#run(),这种情况下的工作线程不会中断,而是等待下一轮执行getTask()方法的时候通过线程池状态判断正常终结该工作线程。
在实际工作当中,对于究竟应该使用哪一种方法去中断线程池,应该结合具体的任务来决定,如果要求任务必须执行完成,那么就是用shutdown()方法。通常也建议使用shutdown()方法,更加优雅。
总结线程池线程执行整体源码流程
- 首先空线程池会去调用
addWorker()方法去创建核心线程. addWorker()过程中使用到了两个自旋去使用 cas 方式修改 ctl 中的线程数量部分.- 走出循环之后使用
Worker内部类利用装饰者模式对 Runnable 对象进行装饰,并且使用线程工厂创建线程. - 通过使用 ReentrantLock 加锁去保证任务可以正常添加,然后进行 start() .
- 核心线程能存活的原因就在于 Worker 内部类中对任务的包装.
- Worker 本身继承了 Runnable 接口,在对任务进行包装之后,将自己作为任务传入线程.
- 在线程池进行 start() 之后调用了自身的 run() 方法,而 run() 方法中又调用了 runWorker() 方法.
runWorker()方法中使用了一个while循环去调用getTask()方法获取阻塞队列中的任务.getTask()方法中使用自旋去调用workQueue#poll()超时方法 或者workQueue#take()阻塞方法 去获取任务.- 等待核心线程都创建之后,再来新任务就会在 execute() 方法中调用
workQueue#offer()方法将新任务加入阻塞队列中 - 如果阻塞队列满了,就会调用
addWorker()方法去生产非核心线程.
- 非核心线程如何回收.
- 在
getTask()方法中返回 null 就会导致循环结束,从而线程结束 - 能够返回 null 的几个条件
- 线程池关闭
- 条件一 :线程池状态 >= SHUTDOWN情况下并且 (线程池状态 >= STOP 或者任务队列为空)
- 条件二 :当前线程数量大于最大线程数量
- 条件三 :核心线程可以超时 或者 当前线程数量已经超过核心线程数量 并且 ( 工作线程数量大于1 或者任务队列无数据 )
- 将 ctl 中属于线程数量的值使用 cas -1
- 最终热恩物结束并回收线程.
- 在
💡任务执行----submit()
一笔带过,暂不深究!
AbstractExecutorService 实现一些携带Future的方法 比如 submit() - invokeAll() -invokeAny()
submit()-原理分析
public <T> Future<T> submit(Callable<T> task) {
// 判断空任务
if (task == null) throw new NullPointerException();
// 将任务封装成一个RunnableFuture任务
// RunnableFuture既是一个Runnable任务 又是一个Future结果
RunnableFuture<T> ftask = newTaskFor(task);
// 调用子类execute()方法执行任务
execute(ftask);
// 把任务当成 一个Future结果返回给到调用者使用
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
invokeAll()源码解析
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException {
// 任务集合非空判断
if (tasks == null)
throw new NullPointerException();
// 生成futures结果集合
ArrayList<Future<T>> futures = new ArrayList<>(tasks.size());
try {
// 遍历任务集合
for (Callable<T> t : tasks) {
// 将任务包装成一个RunnableFuture接口 实现类为FutureTask
// RunnableFuture接口是集成了Future接口以及Runnable
// 也就是说FutureTask既作为一个Runnable任务 同时也作为一个Future提供结果返回
RunnableFuture<T> f = newTaskFor(t);
// 加入到结果集合
futures.add(f);
// 加入到线程池中execute执行 execute源码已经看完了
execute(f);
}
// 循环结果集合
for (int i = 0, size = futures.size(); i < size; i++) {
// 获取每个future
Future<T> f = futures.get(i);
// 判断线程是否执行完 如果执行完了 会返回true 如果没有执行完会返回false
if (!f.isDone()) {
// 没有执行完在这里阻塞获取结果
try { f.get(); }
catch (CancellationException | ExecutionException ignore) {}
}
}
// 等到所有任务都获取到结果了 返回结果集合
return futures;
} catch (Throwable t) {
// 报错了取消任务
cancelAll(futures);
throw t;
}
}
解析doInvokeAny()源码
// 方法入参 tasks: 任务集合 timed:是否可以超时 nanos:超时时间 单位纳秒
private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks, boolean timed, long nanos)
throws InterruptedException, ExecutionException, TimeoutException {
// 常规判空
if (tasks == null)
throw new NullPointerException();
// 获取任务数量 用于后面判断
int ntasks = tasks.size();
if (ntasks == 0)
throw new IllegalArgumentException();
// 创建结果集合
ArrayList<Future<T>> futures = new ArrayList<>(ntasks);
// 创建一个执行服务ecs ExecutorCompletionService在下面单独解析
// 内部存在三个变量
// executor:执行任务的执行线程池
// aes:用来生成包装任务的线程池 如果构造器传入的this是AbstractExecutorService体系的线程池 那么就使用这个this
// 否则会使用默认的FutureTask包装任务
// completionQueue:任务队列 在线程执行完成后会将通过一系列调用将任务本身塞进任务队列中
ExecutorCompletionService<T> ecs =
new ExecutorCompletionService<T>(this);
try {
// 声明异常
ExecutionException ee = null;
// 定义超时时间 如果不允许为超时 则为 0
final long deadline = timed ? System.nanoTime() + nanos : 0L;
// 获取任务集合的迭代器
Iterator<? extends Callable<T>> it = tasks.iterator();
// 使用ecs的submit执行任务并将结果future加入到结果集合中
futures.add(ecs.submit(it.next()));
// 任务数量-1
--ntasks;
// 执行任务的判断 因为上面去执行了一个任务 所以这里默认为1
int active = 1;
for (;;) {
// 从ecs中的completionQueue结果队列中poll一个结果
Future<T> f = ecs.poll();
// 如果poll不到结果的情况下
if (f == null) {
// 判断当前是否还有可以执行的任务
if (ntasks > 0) {
// 执行任务数量-1
--ntasks;
// 再使用ecs的submit执行任务并将结果future加入到结果集合中
futures.add(ecs.submit(it.next()));
// 在执行的任务数量+1
++active;
}
// ntasks <=0的情况下 代表没有任务可以执行了
// 那么判断是不是也没有在执行的任务了
// 如果没有任务在执行了 直接跳出死循环
else if (active == 0)
break;
// 如果没有任务可以执行了 并且 任务还在执行中
// 那么判断是否可以超时
else if (timed) {
// ntasks > 0并且active == 0 代表所有任务都已经在执行中了
// 可以超时的情况下 这里直接poll超时时间
f = ecs.poll(nanos, NANOSECONDS);
// 如果超时后还没有返回结果
if (f == null)
// 超时异常 直接结束方法
throw new TimeoutException();
// 如果有结果 记录时间
nanos = deadline - System.nanoTime();
}
// 如果所有任务都在执行中了 并且不可超时
// 在这里直接使用take()阻塞获取结果
else
f = ecs.take();
}
// 获取到结果后如果结果不为空
if (f != null) {
// 执行完后执行中的线程数量-1
--active;
try {
// 返回结果值
return f.get();
} catch (ExecutionException eex) {
ee = eex;
} catch (RuntimeException rex) {
ee = new ExecutionException(rex);
}
}
}
// 这里的情况 我也搞不清楚为什么要写这一段 个人感觉是
// 当--active将在执行的线程数量-1之后
// f.get()出现奇奇怪怪的问题导致没有结束返回时进行了下一次循环
// 下一次循环通过 active == 0 进行了break跳出循环
// 导致没有返回结果 就会执行到这里来 通过ee==null抛出异常
if (ee == null)
ee = new ExecutionException();
throw ee;
} finally {
// 一个任务返回结果后
// return f.get()
// 将所有任务进行取消
cancelAll(futures);
}
}
invokeAll
将任务全部使用FutureTask进行包装 然后加入到一个arrayList中 同时execute 全部执行后在开一个循环 从arrayList中拿出future进行get拿值
invokeAny
和invokeAll类似,但是使用了一个ExecutorCompletionService也就是ecs对线程池进行了一个包装
💡其他
线程池本身提供了大量数据统计相关的方法、扩容方法、预创建方法等等,这些方法的源码并不复杂,这里不做展开分析。
核心线程相关:
getCorePoolSize():获取核心线程数。setCorePoolSize():重新设置线程池的核心线程数。prestartCoreThread():预启动一个核心线程,当且仅当工作线程数量小于核心线程数量。prestartAllCoreThreads():预启动所有核心线程。
线程池容量相关:
getMaximumPoolSize():获取线程池容量。setMaximumPoolSize():重新设置线程池的最大容量。
线程存活周期相关:
setKeepAliveTime():设置空闲工作线程的存活周期。getKeepAliveTime():获取空闲工作线程的存活周期。
其他监控统计相关方法:
getTaskCount():获取所有已经被执行的任务总数的近似值。getCompletedTaskCount():获取所有已经执行完成的任务总数的近似值。getLargestPoolSize():获取线程池的峰值线程数(最大池容量)。getActiveCount():获取所有活跃线程总数(正在执行任务的工作线程)的近似值。getPoolSize():获取工作线程集合的容量(当前线程池中的总工作线程数)。
任务队列操作相关方法:
purge():移除任务队列中所有是Future类型并且已经处于Cancelled状态的任务。remove():从任务队列中移除指定的任务。BlockingQueue<Runnable> getQueue():获取任务队列的引用。
有部分属性值的设置有可能影响到线程池中的状态或者工作线程的增减等,例如核心线程数改变,有可能会直接增减 Worker,这里就以ThreadPoolExecutor#setCorePoolSize() 为例:
- 如果新值大于旧值,则从队列取任务
- 如果新值小于旧值,则在有线程空闲时,减少池中的线程数
// 设置核心线程数量
public void setCorePoolSize(int corePoolSize) {
// 设置的核心线程数小于0则抛异常.
if (corePoolSize < 0)
throw new IllegalArgumentException();
// delta = 设置的核心线程数和现存的核心线程数的差值.
int delta = corePoolSize - this.corePoolSize;
this.corePoolSize = corePoolSize;
// 如果当前线程池工作线程的总量大于将要设置的核心线程数, 则中断所有的工作线程.
if (workerCountOf(ctl.get()) > corePoolSize)
interruptIdleWorkers();
// 传入核心线程数和现存的核心线程数的差值大于0,也就是核心线程扩容
else if (delta > 0) {
// We don't really know how many new threads are "needed".
// As a heuristic, prestart enough new workers (up to new
// core size) to handle the current number of tasks in
// queue, but stop if queue becomes empty while doing so.
// 计算核心线程扩容数量与任务队列中任务个数的最小值.(队列大小是否可以取任务)
int k = Math.min(delta, workQueue.size());
while (k-- > 0 && addWorker(null, true)) {
// // 如果任务队列为空,则跳出循环 (队列有任务就取,否则break)
if (workQueue.isEmpty())
break;
}
}
}
这里else if (delta > 0)后面的代码块中有一段描述,翻译一下:我们并不知道真正情况下”需要”多少新的工作线程。作为一种启发式处理方式,预先启动足够多的新的工作线程(直到数量为核心线程池大小)来处理队列中当前的任务,但如果在这样做时队列变为空,则停止创建新的工作线程。
总结
线程池的实现原理主要分为4个核心步骤,先判断线程数量是否超过核心线程数,然后再判断任务队列是否已经满了,再判断线程数会不会超过设置的最大线程数,最后执行拒绝策略。
无论是读写锁的实现,还是线程池的实现,Doug Lea都使用了将一个int型的变量按照高低位拆分的技巧,这种思想很值得学习,不仅是因为设计巧妙,还因为在计算机中位运算的执行效率更高。
为什么是先判断任务队列有没有满,再判断线程数有没有超过最大线程数?而不是先判断最大线程数,再判断任务队列是否已满?
因为当需要创建线程的时候,都会调用addWorker()方法,在addWorker()的后半部分的逻辑中,会调用mainLock.lock()方法来获取全局锁,而获取锁就会造成一定的资源争抢。如果先判断最大线程数,再判断任务队列是否已满,这样就会造成线程池原理的4个步骤中,第1步判断核心线程数时要获取全局锁,第2步判断最大线程数时,又要获取全局锁,这样相比于先判断任务队列是否已满,再判断最大线程数,就可能会多出一次获取全局锁的过程。因此在设计线程池,为了尽可能的避免因为获取全局锁而造成资源的争抢,所以会先判断任务队列是否已满,再判断最大线程数。
LinkedBlockingQueue的吞吐量比ArrayBlockingQueue的吞吐量要高。前者是基于链表实现的,后者是基于数组实现的,正常情况下,不应该是数组的性能要高于链表吗?
这是因为LinkedBlockingQueue的读和写操作使用了两个锁,takeLock和putLock,读写操作不会造成资源的争抢。而ArrayBlockingQueue的读和写使用的是同一把锁,读写操作存在锁的竞争。因此LinkedBlockingQueue的吞吐量高于ArrayBlockingQueue。
附录
什么是游离对象
如果不进行垃圾回收,内存迟早会被消耗完。垃圾回收机制的引入可以有效的防止内存泄漏,保证内存的有效使用。也减轻了 Java 程序员对内存管理的工作量。
内存泄露:只该内存空间使用完毕后未回收,在不涉及复杂数据结构的一般情况下, Java 的内存泄露表现为一个内存对象的生命周期超出了程序需要它的时间长度,也将其称为 "游离对象" 。

浙公网安备 33010602011771号