该篇算是对Java线程池解析(上)的一个补充,从源码的角度分析了下ThreadPoolExecutor。建议先看下Java线程池解析(上)在看该篇,这样会顺畅不少。如果文中有任何疑问欢迎留言讨论。觉得好的话欢迎转发评论。
我们先看下类图结构:
![线程池类图结构](https://i-blog.csdnimg.cn/blog_migrate/fefde9ff4353399f6c504977f9d37dff.png)
一.
线程池成员变量ctl是Integer原子变量类型,使用一个变量同时记录线程池状态和线程池中工作线程(worker线程)个数,java中int变量是32位,如下面代码所示,其中高三位表示线程池状态,后边的29位用来记录worker线程数
//用来标记线程池的状态(高三位);线程个数(后29位)
//默认是RUNNING状态,线程个数0
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;
线程池的主要状态如下:
//接收新任务并且处理阻塞队列里的任务
private static final int RUNNING = -1 << COUNT_BITS;
//拒绝新任务但是处理阻塞队列里的任务
private static final int SHUTDOWN = 0 << COUNT_BITS;
//拒绝新任务并且抛弃阻塞队列里的任务,同时中断正在处理的任务
private static final int STOP = 1 << COUNT_BITS;
//所有的任务都执行完(包括阻塞队列里的任务),当前线程池活动线程为0,将要调用terminated方法
private static final int TIDYING = 2 << COUNT_BITS;
//线程的终止状态,terminated方法执行完的状态
private static final int TERMINATED = 3 << COUNT_BITS;
//用来记录当前线程池工作的线程
private final HashSet<Worker> workers = new HashSet<Worker>();
这里需要说明的是 terminated方法为 线程池结束后调用的方法,钩子方法,自己去重写
获取线程池状态的函数有:
// 获取高三位 线程状态
private static int runStateOf(int c) { return c & ~CAPACITY; }
//获取低29位线程个数值
private static int workerCountOf(int c) { return c & CAPACITY; }
//计算新的状态值(根据高三位和 低29位)
private static int ctlOf(int rs, int wc) { return rs | wc; }
从类图中我们知道ThreadPoolExecutor有一个内部类worker,该类继承自AbstractQueuedSynchronizer和Runnable,该类是具体承载任务的线程对象,继承aqs主要是实现了一个简单的不可重入独占锁,我们先看下源码:
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
/** Thread this worker is running in. Null if factory fails. */
final Thread thread;
/** 要执行的任务(ThreadPoolExecutor.execute提交的任务),可能为空*/
Runnable firstTask;
/** Per-thread task counter */
volatile long completedTasks;
// 使用ThreadFactory构造Thread,这个构造的Thread内部的Runnable就是本身,也就是Worker。
//所以得到Worker的thread并start的时候,会执行Worker的run方法,也就是执行ThreadPoolExecutor的runWorker方法
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** 启动一个线程 */
public void run() {
/** 这里调用的其实是 ThreadPoolExecutor.runWorker 方法 */
runWorker(this);
}
protected boolean isHeldExclusively() { return getState() != 0; }
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
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实现独占锁 主要是为了标识worker线程是否为空闲线程,如果这个线程正在处理具体的任务 那么就不是空闲线程
二.提交任务到线程池的源码解析
提交任务到线程池,ThreadPoolExecutor提供了四种实现方式,源码如下:
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
上面三种是有返回值的,我看可以看到 最后都是调用的execute方法,来看一下第四种提交任务的execute方法
//提交任务到线程池的逻辑
public void execute(Runnable command) {
//如果任务为null,则抛出空指针
if (command == null)
throw new NullPointerException();
//获取当前线程状态 + 线程个数 组合值
int c = ctl.get();
//当前线程个数 是否小于corePoolSize ,小于则开启新线程执行任务
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//如果当前线程池状态为running 添加任务到阻塞队列
if (isRunning(c) && workQueue.offer(command)) {
//二次检查线程池状态
int recheck = ctl.get();
//如果线程池状态不为running 则移除刚添加到阻塞队列里的任务,并执行拒绝策略
if (! isRunning(recheck) && remove(command))
reject(command);
//如果当前线程池线程为空 则添加一个线程(走到这里说明已经添加任务到阻塞队列且线程池状态为running,出现这种情况比如核心线程数为0)
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//添加任务到任务队列失败,尝试创建新线程(这里 线程数 < maximumPoolSize才会创建,否则创建失败),失败执行拒绝策略
else if (!addWorker(command, false))
//实现 RejectedExecutionHandler接口(可以自己重写)
reject(command);
}
提交任务到线程池大概分为三步:
- 如果当前正在执行任务的Worker线程数量比corePoolSize(基本大小)要小。直接创建一个新的Worker线程执行任务,会调用addWorker方法,尝试创建一个worker线程
- 如果当前正在执行任务的 Worker线程数量 >= corePoolSize(基本大小) 且 线程池的运行状态为RUNNING,将任务放到阻塞队列里 ,否则执行第3步。丢到阻塞队列之后,还需要再做一次验证(丢到阻塞队列之后可能另外一个线程关闭了线程池或者刚刚加入到队列的线程死了)。如果这个时候线程池不在RUNNING状态,把刚刚丢入队列的任务remove掉,调用reject方法,否则查看Worker数量,如果Worker数量为0,起一个新的Worker去阻塞队列里拿任务执行
- 添加任务到阻塞队列失败,尝试创建新线程(这里 线程数 < maximumPoolSize才会创建,否则创建失败),失败执行拒绝策略
我们看下addWorker方法:
// 两个参数,firstTask表示需要跑的任务。boolean类型的core参数为true的话表示使用线程池的基本大小,为false使用线程池最大大小
// 返回值是boolean类型,true表示新任务被接收了,并且执行了。否则是false
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
//获取当前线程状态 + 线程个数 组合值
int c = ctl.get();
//获取当前线程状态
int rs = runStateOf(c);
// 官方注: 仅在必要时检查队列是否为空
// 这个判断转换成 rs >= SHUTDOWN && (rs != SHUTDOWN || firstTask != null || workQueue.isEmpty)。
// 概括为3个条件:
// 1. 线程池不在RUNNING状态并且状态是STOP、TIDYING或TERMINATED中的任意一种状态
// 2. 线程池不在RUNNING状态,线程池接受了新的任务
// 3. 线程池不在RUNNING状态,阻塞队列为空。 满足这3个条件中的任意一个的话,拒绝执行任务
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
// 线程池线程个数
int wc = workerCountOf(c);
// 如果线程池线程数量超过线程池最大容量
// 或者线程数量超过了基本大小(core参数为true,core参数为false的话判断超过最大大小)
// 超过的话 直接返回false
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 没有超过各种大小的话,cas操作线程池线程数量+1,cas成功的话跳出循环
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
// 如果状态改变了,重新循环操作
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
// 任务是否成功启动标识
boolean workerStarted = false;
// 任务是否添加成功标识
boolean workerAdded = false;
Worker w = null;
try {
//构建一个 worker 对象,创建一个工作线程,Worker实现了Runnable
//Worker.firstTask 暂存了 firstTask任务;
w = new Worker(firstTask);
//new Worker 时使用ThreadFactory构造了一个Thread
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());
//当前线程状态为RUNNING 或者 为 SHUTDOWN 且 firstTask ==null
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
//把通过new Worker创建的工作线程 放到set中存起来
workers.add(w);
int s = workers.size();
//largestPoolSize用来记录活跃线程曾经最大的值
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
//任务添加成功,启动刚才创建好的工作线程
//t.start 启动worker线程
if (workerAdded) {
t.start();// 注1
workerStarted = true;
}
}
} finally {
//任务启动失败
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
这里主要是通过addWorker方法 创建一个worker线程,并维护进workers集合然后启动该worker线程,
在上文中介绍ThreadPoolExecutor.Worker 类的时候我们知道Worker 继承自Runnable 接口,所以注1中这里是启动worker线程我们在看下Worker.run 方法:
/** 启动一个线程 */
public void run() {
/** 这里调用的其实是 ThreadPoolExecutor.runWorker 方法 */
runWorker(this);
}
这里是委托给runWorker方法,我们看下源码:
// worker线程处理 提交的任务
final void runWorker(Worker w) {
//获取当前线程
Thread wt = Thread.currentThread();
//要执行的 Runnable任务,可能为空
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//执行当前任务或者 执行阻塞队列中的任务(如果阻塞队列为空 将当前线程挂起阻塞队列上(需要符合条件,具体见getTask))
while (task != null || (task = getTask()) != null) { //注2
// 如果拿到了任务,给自己上锁,表示当前Worker已经要开始执行任务了,已经不是闲置Worker(线程池关闭的时候需要用到该状态)
w.lock();//注3
// 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
// 在执行任务之前先做一些处理。
// 1. 如果线程池已经处于STOP,TIDYING,TERMINATED状态并且当前线程没有被中断,中断线程
//2. 如果线程池还处于RUNNING或SHUTDOWN状态,并且当前线程已经被中断了,重新检查一下线程池状态,如果处于STOP状态并且没有被中断,那么中断线程
if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
//任务执行前 做点什么事~;空方法,可以自己去重写
beforeExecute(wt, task);
Throwable thrown = null;
try {
//真正的去执任务,这里是调用run方法 而不是start方法
task.run();// 注:4
//任务执行发生的异常全部抛出,不在runWorker中处理
} 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(task, thrown);
}
} finally {
task = null;
// 记录当前worker线程执行任务的个数
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
// 回收worker线程
processWorkerExit(w, completedAbruptly);
}
}
runWorker方法的作用主要是用来处理线程池提交的任务(包括阻塞队列)。见注2
如果阻塞队列里的任务也处理空就把当前线程挂起在阻塞队列上(当且线程池处于RUNNING状态 且 worker线程数小于corePoolSize),直到被中断或者获取到任务
这里还需要注意的是如果worker线程开始处理任务以后将当前worker线程置为非闲置状态(给自己加锁)。在回收worker线程时需要判断当前worker线程是否正在处理任务。注 3
getTask用来获取阻塞队列的任务并我们看一下getTask方法是如何获得任务的:
private Runnable getTask() {
// 用来标识阻塞队列poll() 超时的情况
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.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
// 当前线程池线程数
int wc = workerCountOf(c);
// 标记从阻塞队列中获取任务时是否设置超时时间
//true: 说明这个worker可能需要回收(allowCoreThreadTimeOut设置为true 或者 当前线程数 > 线程池核心线程数)
//false: 这个worker会一直存在,并且阻塞当前线程等待阻塞队列中有数据
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//worker线程大于maximumPoolSize 或者 在keepAliveTime时间内阻塞队列为空
if ((wc > maximumPoolSize || (timed && timedOut))
// 线程池的线程数 大于1或者worker阻塞队列为空(在阻塞队列不为空时,需要保证至少有一个worker)
&& (wc > 1 || workQueue.isEmpty())) {
// worker线程数量减一,返回null,之后会进行Worker回收工作
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// timed == true时,使用poll方法,worker线程keepAliveTime这么多时间没有获取到阻塞队列里的任务 返回r == null
//否则使用take方法一直阻塞等待阻塞队列新进数据
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
//用来标记超时后还没获取到阻塞队列任务的情况
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
说明下getTask方法,获取阻塞队列中的任务, 如果阻塞队列为空将当前worker线程挂起,直到阻塞队列有任务了
如果getTask方法返回null 则对当前worker线程回收,返回null 的情况有:
- 当 worker线程数 大于corePoolSize时 且 超过阻塞时间超过keepAliveTime没有获取到任务时,唤醒当前worker线程,并返回null 释放掉当前线程资源
- 线程池处于STOP状态
- 线程池处于SHUTDOWN状态并且阻塞队列为空
- Worker个数 大于 maximumPoolSize(如果maximumPoolSize <= 1,还需要判断阻塞队列是否为空)
当getTask线程返回null后 ,runWorker方法将会调用processWorkerExit方法回收worker线程,代码如下:
private void processWorkerExit(Worker w, boolean completedAbruptly) {
//如果当前worker线程执行的任务发生了异常,需要减少线程池线程数量(workCount--)
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 销毁当前worker线程时, 记录当前worker线程处理任务的总数
completedTaskCount += w.completedTasks;
// 从workers集合中移除该worker
workers.remove(w);
} finally {
mainLock.unlock();
}
// 尝试结束线程池
tryTerminate();
int c = ctl.get();
// 如果线程池还处于RUNNING或者SHUTDOWN状态
if (runStateLessThan(c, STOP)) {
//worker线程是正常回收
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
//阻塞队列不为空的情况下 worker线程最少保留一个
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
//当前worker线程执行的任务发生了异常时,新建一个worker线程替换因异常结束的线程
addWorker(null, false);
}
}
说明:
processWorkerExit第一个参数为要回收的worker线程,第二个参数completedAbruptly为true 说明worker线程是因为执行的任务发生了异常而结束的,那么就尝试创建一个新的worker线程
正常情况下回收线程时每次都会调用下tryTerminate()方法尝试结束当前线程池,代码如下:
//尝试结束 当前线程池,在worker线程被回收时都会尝试结束线程池
final void tryTerminate() {
for (;;) {
int c = ctl.get();
// 满足3个条件中的任意一个,不终止线程池
// 1. 线程池还在运行,不能终止
// 2. 线程池处于TIDYING或TERMINATED状态,说明已经在关闭了,不允许继续处理
// 3. 线程池处于SHUTDOWN状态并且阻塞队列不为空,这时候还需要处理阻塞队列的任务,不能终止线程池
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
// 走到这一步说明线程池已经不在运行,阻塞队列已经没有任务,但是还要回收正在工作的Worker
if (workerCountOf(c) != 0) { // Eligible to terminate
// 中断闲置Worker,直到回收全部的Worker。
//这里仅中断一个,中断之后结束interruptIdleWorkers方法,中断了Worker之后,Worker会回收,然后还是会调用tryTerminate方法,如果还有闲置线程,那么继续中断
interruptIdleWorkers(ONLY_ONE);//ONLY_ONE == true
return;
}
// 到这里说明worker线程已经全部回收了,并且线程池已经不在运行,阻塞队列已经没有任务。可以准备结束线程池了
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// cas操作,将线程池状态改成TIDYING
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
//线程池退出前 做点什么事~;空方法,可以自己去重写
terminated();
} finally {
// terminated方法调用完毕之后,状态变为TERMINATED
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
//中断闲置线程
//onlyOne为true:只中断一个闲置worker线程(该线程被回收的时候还会调用tryTerminate,如果有闲置线程还是会被回收的,所以该行为会传播)
//onlyOne为false: 中断所有闲置线程
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
到这里 整个线程池的任务提交过程就讲完了,下边看下线程池关闭的过程
三.线程池关闭
1.shutdown()
线程池不在接收新任务,但是还是会处理阻塞队列中的任务
这里只会中断闲置线程(那些挂起在阻塞队列上的线程)
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 检查关闭线程池的权限
checkShutdownAccess();
// 把线程池状态更新到SHUTDOWN
advanceRunState(SHUTDOWN);
// 中断闲置的Worker
interruptIdleWorkers(false);
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
//尝试结束线程池,上文中有讲到
tryTerminate();
}
interruptIdleWorkers源码在上文中已经解释了 这里就不多说了
2.shutdownNow
抛弃阻塞队列中的任务,不在接收新任务,并中断所有worker线程
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 把线程池状态更新到STOP
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
//中断所有worker线程
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}