线程池源码解析
线程池解决两个问题:
-
通过减少任务间的调度开销 (主要是通过线程池中的线程被重复使用的方式),来提高大量任务时的执行性能;
-
提供了一种方式来管理线程和消费,维护基本数据统计等工作,比如统计已完成的任务数;
Executors 为常用的场景设定了可直接初始化线程池的方法
- Executors#newCachedThreadPool 无界的线程池,并且可以自动回收;
- Executors#newFixedThreadPool 固定大小线程池;
- Executors#newSingleThreadExecutor 单个线程的线程池;
整体框架
线程池整体架构图:
ThreadPoolExecutor类结构图:
- Executor:定义 execute 方法来执行任务,入参是 Runnable,无出参
public interface Executor {
void execute(Runnable command);
}
- ExecutorService:Executor 的功能太弱,ExecutorService 丰富了对任务的执行和管理的
// 在完成已提交的任务后封闭办事,不再接管新任务,
void shutdown();
// 停止所有正在履行的任务并封闭办事。
List<Runnable> shutdownNow();
// 测试是否该ExecutorService已被关闭。
boolean isShutdown();
// 所有的任务是否都已经终止,是的话,返回 true
boolean isTerminated();
// 在超时时间内,等待剩余的任务终止
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
// 提交有返回值的任务,使用 get 方法可以阻塞等待任务的执行结果返回
<T> Future<T> submit(Callable<T> task);
// 提交有返回值的任务,使用 get 方法可以阻塞等待任务的执行结果返回
<T> Future<T> submit(Runnable task, T result);
// 提交没有返回值的任务,如果使用 get 方法的话,任务执行完之后得到的是 null 值
Future<?> submit(Runnable task);
// 给定任务集合,返回已经执行完成的 Future 集合,每个返回的 Future 都是 isDone = true 的状态
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
// 给定任务中有一个执行成功就返回,如果抛异常,其余未完成的任务将被取消
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
线程池状态扭转图:
-
RUNNING
线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行 处理。
-
SHUTDOWN
线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务
-
STOP
线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
-
TIDYING
当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING 状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在 ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理; 可以通过重载terminated()函数来实现。
-
TERMINATED
线程池彻底终止,就变成TERMINATED状态。
重要属性:
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;
ctl 是对线程池的运行状态和线程池中有效线程的数量进行控制的一个字段, 它包含两 部分的信息: 线程池的运行状态 (runState) 和线程池内有效线程的数量 (workerCount),这 里可以看到,使用了Integer类型来保存,高3位保存runState,低29位保存 workerCount。COUNT_BITS 就是29,CAPACITY就是1左移29位减1(29个1),这个常 量表示workerCount的上限值,大约是5亿。
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; }
- runStateOf: 获取运行状态;
- workerCountOf: 获取活动线程数;
- ctlOf: 获取运行状态和活动线程数的值。
Worker类
-
实现了 Runnable 接口,实现了 run 方法;
-
初始化时 this.thread = getThreadFactory ().newThread (this)
-
实现了 AQS,所以其本身也是一个锁,其在执行任务的时候,会锁住自己,防止被中断任务执行完成之后,会释放自己。
// 线程池中任务执行的最小单元
// Worker 继承 AQS,具有锁功能
// Worker 实现 Runnable,本身是一个可执行的任务
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
// 任务运行的线程
final Thread thread;
// 需要执行的任务
Runnable firstTask;
// 非常巧妙的设计,Worker本身是个 Runnable,把自己作为任务传递给 thread
// 内部有个属性又设置了 Runnable
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
// 把 Worker 自己作为 thread 运行的任务
this.thread = getThreadFactory().newThread(this);
}
/** Worker 本身是 Runnable,run 方法是 Worker 执行的入口, runWorker 是外部的方法 */
public void run() {
runWorker(this);
}
private static final long serialVersionUID = 6138294804551838833L;
// Lock methods
// 0 代表没有锁住,1 代表锁住
protected boolean isHeldExclusively() {
return getState() != 0;
}
// 尝试加锁,CAS 赋值为 1,表示锁住
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// 尝试释放锁,释放锁没有 CAS 校验,可以任意的释放锁
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继承了AQS,使用AQS来实现独占锁的功能。为什么不使用ReentrantLock来 实现呢?可以看到tryAcquire方法,它是不允许重入的,而ReentrantLock是允许重入的:
- lock方法一旦获取了独占锁,表示当前线程正在执行任务中;
- 如果正在执行任务,则不应该中断线程;
- 如果该线程现在不是独占锁的状态,也就是空闲的状态,说明它没有在处理任务, 这时可以对该线程进行中断;
- 线程池在执行shutdown方法或tryTerminate方法时会调用interruptIdleWorkers 方法来中断空闲的线程,interruptIdleWorkers方法会使用tryLock方法来判断线程 池中的线程是否是空闲状态;
- 之所以设置为不可重入,是因为我们不希望任务在调用像setCorePoolSize这样的 线程池控制方法时重新获取锁。如果使用ReentrantLock,它是可重入的,这样如果 在任务中调用了如setCorePoolSize这类线程池控制的方法,会中断正在运行的线 程。
所以,Worker继承自AQS,用于判断线程是否空闲以及是否可以被中断。 此外,在构造方法中执行了setState(-1); 把state变量设置为-1,为什么这么做呢? 是因为AQS中默认的state是0,如果刚创建了一个Worker对象,还没有执行任务时,这时就不应该被中断。
线程池的创建
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
-
corePoolSize
线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize;如果当前线程数为corePoolSize,继续提交的任务被保存到 阻塞队列中,等待被执行;如果执行了线程池的prestartAllCoreThreads()方法,线程池会 提前创建并启动所有核心线程。
-
maximumPoolSize
线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize;
-
keepAliveTime
线程池维护线程所允许的空闲时间。当线程池中的线程数量大于corePoolSize的时 候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待 的时间超过了keepAliveTime;
-
unit
keepAliveTime的单位;
-
workQueue
用来保存等待被执行的任务的阻塞队列,且任务必须实现Runable接口,在JDK中提供了如下阻塞队列:
- ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
- LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;
- SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到 另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于 LinkedBlockingQuene;
- priorityBlockingQuene:具有优先级的无界阻塞队列;
-
threadFactory
它是ThreadFactory类型的变量,用来创建新线程。默认使用 Executors.defaultThreadFactory() 来创建线程。使用默认的ThreadFactory来创建线程 时,会使新创建的线程具有相同的NORM_PRIORITY优先级并且是非守护线程,同时也设 置了线程的名称。
-
handler
线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:
- AbortPolicy: 直接抛出异常,默认策略;
- CallerRunsPolicy: 用调用者所在的线程来执行任务;
- DiscardOldestPolicy: 丢弃阻塞队列中靠最前的任务,并执行当前任务;
- DiscardPolicy: 直接丢弃任务;
也可以根据应用场景实现RejectedExecutionHandler接口,自定义饱和策略,如 记录日志或持久化存储不能处理的任务。
线程池监控
public long getTaskCount() //线程池已执行与未执行的任务总数
public long getCompletedTaskCount() //已完成的任务数
public int getPoolSize() //线程池当前的线程数
public int getActiveCount() //线程池中正在执行任务的线程数量
任务提交
AbstractExecutorService#submit
- 把 Runnable 和 Callable 都转化成 FutureTask
- 使用 execute 方法执行 FutureTask。
execute
-
工作的线程小于核心线程数,创建新的线程; 如果添加失败,则重新获取ctl值
-
如果当前线程池是运行状态并且任务添加到队列成功。
-
然后会重新判断运行状态,不是运行状态尝试拒绝任务;
-
如果发现可运行的线程数是 0,就初始化一个线程
-
-
队列满了,开启线程到 maxSize,如果失败直接拒绝,
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
// clt记录着runState和workerCount
int c = ctl.get();
/*
工作的线程小于核心线程数,创建新的线程,成功返回,失败不抛异常
workerCountOf方法取出低29位的值,表示当前活动的线程数;
如果当前活动线程数小于corePoolSize,则新建一个线程放入线程池中;并把任务添加到该线程中。
*/
if (workerCountOf(c) < corePoolSize) {
/*
addWorker中的第二个参数表示限制添加线程的数量是根据corePoolSize 来判断还是maximumPoolSize来判断;
如果为true,根据corePoolSize来判断;
如果为false,则根据maximumPoolSize来判断
*/
if (addWorker(command, true))
return;
// 如果添加失败,则重新获取ctl值,线程池状态可能发生变化
c = ctl.get();
}
/*
工作的线程大于等于核心线程数,或者新建线程失败
线程池状态正常,并且可以入队的话,尝试入队列
如果当前线程池是运行状态并且任务添加到队列成功
*/
if (isRunning(c) && workQueue.offer(command)) {
// 重新获取ctl值
int recheck = ctl.get();
/*
如果线程池状态异常 尝试从队列中移除任务,可以移除的话就拒绝掉任务
再次判断线程池的运行状态,如果不是运行状态,由于之前已经把command添加到workQueue中了,这时需要移除该command
执行过后通过handler使用拒绝策略对该任务进行处理,整个方法返回
*/
if (!isRunning(recheck) && remove(command))
reject(command);
// 发现可运行的线程数是 0,就初始化一个线程,这里是个极限情况,入队的时候,突然发现可用线程都被回收了
else if (workerCountOf(recheck) == 0)
/*
Runnable是空的,不会影响新增线程,但是线程在 start 的时候不会运行
Thread.run() 里面有判断
获取线程池中的有效线程数,如果数量是0,则执行addWorker方法
这里传入的参数表示:
1. 第一个参数为null,表示在线程池中创建一个线程,但不去启动;
2. 第二个参数为false,将线程池的有限线程数量的上限设置为maximumPoolSize,添加线程时根据maximumPoolSize来判断;
如果判断workerCount大于0,则直接返回,在workQueue中新增的command会在将来的某个时刻被执行。
*/
addWorker(null, false);
}
/*
3.队列满了,开启线程到 maxSize,如果失败直接拒绝,
如果执行到这里,有两种情况:
1. 线程池已经不是RUNNING状态;
2. 线程池是RUNNING状态,但workerCount >= corePoolSize并且 workQueue已满。
这时,再次调用addWorker方法,但第二个参数传入为false,将线程池的有限 线程数量的上限设置为maximumPoolSize;
如果失败则拒绝该任务
*/
else if (!addWorker(command, false))
reject(command);
}
添加工作线程 addWorker
-
校验,下面情况不需要添加工作线程
1)校验线程状态
>= SHUTDOWN
(中断中),firstTast != null
(不接受任务),workQueue.isEmpty()
(队列没有任务)2) 校验线程数量
线程数大于线程池容量;添加核心线程:大于核心线程数;添加最大线程:大于最大线程数;
-
创建工作线程
-
启动线程
// 结合线程池的情况看是否可以添加新的 worker
// firstTask 不为空可以直接执行,为空执行不了,Thread.run()方法有判断,Runnable为空不执行
// core 为 true 表示线程最大新增个数是 coresize,false 表示最大新增个数是 maxsize
// 返回 true 代表成功,false 失败
// break retry 跳到retry处,且不再进入循环
// continue retry 跳到retry处,且再次进入循环
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
// 1. 先是各种状态的校验
for (;;) {
int c = ctl.get();
// 获取运行状态
int rs = runStateOf(c);
/*
1. 如果rs >= SHUTDOWN,则表示此时不再接收新任务;
2. 接着判断以下3个条件,只要有1个不满足,则返回false:
1) rs == SHUTDOWN,这时表示关闭状态,不再接受新提交的任务,但却可以继续处理阻塞队列中已保存的任务
2) firsTask为空
3) 阻塞队列不为空
首先考虑rs == SHUTDOWN的情况
这种情况下不会接受新提交的任务,所以在firstTask不为空的时候会返回false;
然后,如果firstTask为空,并且workQueue也为空,则返回false
因为队列中已经没有任务了,不需要再添加线程了
*/
if (rs >= SHUTDOWN &&
!(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()))
return false;
for (;;) {
// 获取线程数
int wc = workerCountOf(c);
/*
如果wc超过CAPACITY,也就是ctl的低29位的最大值(二进制是29个1),返回false;
这里的core是addWorker方法的第二个参数,
如果为true表示根据corePoolSize来比较,如果为false则根maximumPoolSize来比较。
*/
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 尝试增加workerCount,如果成功,则跳出第一个for循环
if (compareAndIncrementWorkerCount(c))
break retry;
// 如果增加workerCount失败,则重新获取ctl的值
c = ctl.get();
// 如果当前的运行状态不等于rs,说明状态已被改变,返回第一个for循环继续执行
if (runStateOf(c) != rs)
continue retry;
}
}
// 2. 新建worker
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 巧妙的设计,Worker 本身是个 Runnable.
// 在初始化的过程中,会把 worker 丢给 thread 去初始化
// 根据firstTask来创建Worker对象
w = new Worker(firstTask);
// 每一个Worker对象都会创建一个线程
final Thread t = w.thread;
if (t != null) {
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());
/*
rs < SHUTDOWN表示是RUNNING状态;
如果rs是RUNNING状态 或者rs是SHUTDOWN状态 并且firstTask为null,向线程池中添加线程。
因为在SHUTDOWN时不会在添加新的任务,但还是会执行workQueue中的任务
*/
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
// workers是一个HashSet
workers.add(w);
// largestPoolSize记录着线程池中出现过的最大线程数量
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
// 3. 启动线程,实际上去执行 Worker.run 方法
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
启动线程 runWorker()
-
获取任务
1) 第一个任务
2)从阻塞队列中拿任务
-
线程STOP, 帮助线程中断
-
执行任务
-
线程销毁
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
// 获取第一个任务
Runnable task = w.firstTask;
//帮助gc回收
w.firstTask = null;
// 允许中断
w.unlock(); // allow interrupts
// 是否因为异常退出循环
boolean completedAbruptly = true;
try {
/*
task 为空的情况:
1:任务入队列了,极限情况下,发现没有运行的线程,于是新增一个线程;
2:线程执行完任务执行,再次回到 while 循环。
如果 task 为空,会使用 getTask 方法阻塞从队列中拿数据,如果拿不到数据,会阻塞住
*/
while (task != null || (task = getTask()) != null) {
//锁住 worker
w.lock();
/*
线程池 stop 中,但是线程没有到达中断状态,帮助线程中断
如果线程池正在停止,那么要保证当前线程是中断状态;
如果不是的话,则要保证当前线程不是中断状态;
这里要考虑在执行该if语句期间可能也执行了shutdownNow方法,shutdownNow方法会 把状态设置为STOP
STOP状态:
不能接受新任务,也不处理队列中的任务,会中断正在处理任务的线程。
*/
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted())
wt.interrupt();
try {
//执行 before 钩子函数
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 {
//执行 after 钩子函数,如果这里抛出异常,会覆盖 catch 的异常
//所以这里异常最好不要抛出来
afterExecute(task, thrown);
}
} finally {
//任务执行完成,计算解锁
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
//做一些抛出异常的善后工作
processWorkerExit(w, completedAbruptly);
}
}
获取任务 getTask()
- 判断线程状态
- 超时处理
- 从阻塞队列中拿任务
// 从阻塞队列中拿任务
private Runnable getTask() {
// 表示上次从阻塞队列中取出任务是否超时
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
/*
线程池关闭 && 队列为空,不需要在运行了,直接放回
如果线程池状态rs >= SHUTDOWN,非RUNNING状态,则需要进行以下判断
1. rs >= STOP, 线程正在stop
2. 阻塞队列为空
如果是以上条件满足,则将workerCount减1,并返回null
因为当前线程池状态是SHOWDOWN或以上时,不允许再向阻塞队列中添加任务
*/
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
/*
true 运行的线程数大于 coreSize || 核心线程也可以被灭亡
timed 变量用于判断是否需要进行超时控制
allowCoreThreadTimeOut 默认是false,就是核心线程数不允许进行超时控制
wc > corePoolSize 表示当前线程数大于核心线程数,对于超过核心线程数的这些线程需要进行超时控制
*/
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 队列以 LinkedBlockingQueue 为例,timedOut 为 true 的话说明下面 poll 方法执行返回的是 null
// 说明在等待 keepAliveTime 时间后,队列中仍然没有数据
// 说明此线程已经空闲了 keepAliveTime 了
// 再加上 wc > 1 || workQueue.isEmpty() 的判断
// 所以使用 compareAndDecrementWorkerCount 方法使线程池数量减少 1
// 并且直接 return,return 之后,此空闲的线程会自动被回收
/*
wc > maximumPoolSize 的情况是因为可能在此方法执行阶段同时执行了setMaximumPoolSize()
timed && timedOut 如果为true,表示当前操作需要进行超时控制,并且上次从阻塞队列中获取任务发生了超时
接下来判断:如果有效线程数 >1, 或者阻塞队列是空的,那么尝试将workerCount减1
如果-1失败,则返回重试
如果wc == 1, 也就说明当前线程是线程池中唯一的一个线程了
*/
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// 从队列中阻塞拿 worker
/*
根据timed来判断
如果为true,则通过阻塞队列进行超时控制,如果在keepAliveTime时间内没有获取任务,则返回null
否则通过take方法,如果这时队列为空,则take方法会阻塞直到队列不为空
*/
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
// 如果 r == null, 说明已超时,此时队列没有数据,timeOut设置为true
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
线程销毁 processWorkerExit
getTask方法返回null时,在runWorker方法中会跳出while循环,然后会执行 processWorkerExit方法。
- 统计任务数,销毁当前线程
- 判断是否需要结束线程池
- 判断是否是RUNNING或SHUTDOWN状态,保证有线程执行任务
private void processWorkerExit(Worker w, boolean completedAbruptly) {
/*
如果 completedAbruptly == true, 说明线程执行时出现了异常,需要将workerCount -1
如果线程执行时没有出现异常,说明在getTask()中已经对workerCount进行了-1操作,这里不必再减
*/
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 统计完成的任务数
completedTaskCount += w.completedTasks;
// 从线程池中移除一个工作线程
workers.remove(w);
} finally {
mainLock.unlock();
}
// 根据线程池状态判断是否结束线程池
tryTerminate();
int c = ctl.get();
/*
当线程池是RUNNING或SHUTDOWN状态时,如果worker是异常结束的,那么直接addWorker
如果allowCoreThreadTimeOut==true,并且等待队列中有任务,至少保留一个worker
如果allowCoreThreadTimeOut==false,workerCount不少于coreSize
*/
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);
}
}