ThreadPoolExecutor源码解读
主要成员
// 该变量保存了两个内容,低29位保存有效的工作线程数量workerCount,高3位保存线程池状态runState
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// ctl变量中保存有效线程数量的位数(即29)
private static final int COUNT_BITS = Integer.SIZE - 3;
// ctl中能保存的有效线程数量的容量值
// 运算过程为1左移29位,也就是00000000 00000000 00000000 00000001 --> 001 0000 00000000 00000000 00000000,
// 再减去1,就是 000 11111 11111111 11111111 11111111,
// 前三位代表线程池运行状态runState,
// 所以这里工作线程的理论最大值就应该是29个1,即536870911。
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
/*========================线程状态保存在高3位===============================*/
// -1在Java底层是由32个1表示,即:11111111 11111111 11111111 11111111
// -1 << COUNT_BITS 左移29位的话,即111 00000 00000000 00000000 00000000,
// 也就是低29位全部为0,高3位为111的话,表示RUNNING状态,即-536870912;
// 同理可知 SHUTDOWN、STOP、TIDYING和TERMINATED的值
private static final int RUNNING = -1 << COUNT_BITS;
// 高3位全部为0的话,表示SHUTDOWN状态,即0。
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 高3位为001的话,表示STOP状态,即536870912;
private static final int STOP = 1 << COUNT_BITS;
// 高3位为010的话,表示TIDYING状态,即1073741824。
private static final int TIDYING = 2 << COUNT_BITS;
// 高3位为011的话,表示TERMINATED状态,即1610612736。
private static final int TERMINATED = 3 << COUNT_BITS;
/*========================ctl操作===============================*/
// ~ 是按位取反的意思,CAPACITY表示的是高位的3个0,和低位的29个1,而~CAPACITY则表示高位的3个1,低位的29个0,
// 然后再与入参c执行按位与操作,即高3位保持原样,低29位全部设置为0,也就获取了线程池的运行状态runState。
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 传入的c代表的是ctl的值,即高3位为线程池运行状态runState,低29位为线程池中当前活动的工作线程数量workerCount.
// 将其与CAPACITY进行与操作&,也就是与000 11111 11111111 11111111 11111111进行与操作,
// c的前三位通过与000进行与操作,无论c前三位为何值,最终都会变成000,也就是舍弃前三位的值,
// 而c的低29位与29个1进行与操作,c的低29位还是会保持原值,
// 这样就从AtomicInteger ctl中解析出了workerCount的值。
private static int workerCountOf(int c) { return c & CAPACITY; }
// 将runState和workerCount做或操作|处理,即用runState的高3位,workerCount的低29位填充的数字,
// 也就是当前ctl的值。默认传入的runState、workerCount分别为RUNNING和0
private static int ctlOf(int rs, int wc) { return rs | wc; }
/*========================状态比较===============================*/
private static boolean runStateLessThan(int c, int s) {return c < s;}
private static boolean runStateAtLeast(int c, int s) {return c >= s;}
private static boolean isRunning(int c) {return c < SHUTDOWN;}
/*========================线程池核心变量===============================*/
//任务队列,用来存放等待执行的任务
private final BlockingQueue<Runnable> workQueue;
//线程池的主要状态锁,对线程池状态(比如线程池大小、runState等)的改变都要使用这个锁
private final ReentrantLock mainLock = new ReentrantLock();
//存放执行任务的工作线程集合
private final HashSet<Worker> workers = new HashSet<Worker>();
//工作线程存活时间,即工作线程从任务队列中获取任务超时时间
private volatile long keepAliveTime;
//是否允许为核心线程设置存活时间
//简单理解为要不要使用keepAliveTime这个参数,在介绍getTask方法是再详细说明
private volatile boolean allowCoreThreadTimeOut;
//线程池核心工作线程数
private volatile int corePoolSize;
//线程池最大工作线程数
private volatile int maximumPoolSize;
//任务拒绝策略,默认是丢弃任务,抛异常
private volatile RejectedExecutionHandler handler;
//线程工厂,用来创建线程
private volatile ThreadFactory threadFactory;
//用来记录线程池中曾经出现过的最大线程数
private int largestPoolSize;
//用来记录已经执行完毕的任务个数
private long completedTaskCount;
任务队列
任务队列(workQueue)是阻塞队列,用来存储等待执行的任务,阻塞队列有以下几种选择:
- ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;
- LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
- synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。
工作线程核心数
工作线程核心数(corePoolSize),在创建了线程池后,默认情况下,线程池中并没有任何工作线程,而是等待有任务到来才创建工作线程去执行任务,当然可以通过调用prestartAllCoreThreads()或者prestartCoreThread()方法预创建工作线程,即在没有任务到来之前就创建corePoolSize个工作线程或者一个工作线程。当线程池中的工作线程数达到corePoolSize后,并且队列未满,则会把到达的任务放到任务队列中。
工作线程最大数
工作线程最大数(maximumPoolSize),它表示在线程池中最多能创建的工作线程数,当工作线程数大于该值,会执行拒绝策略。
工作线程存活时间
工作线程存活时间(keepAliveTime)指的是工作线程从任务队列中获取任务的超时时间。默认情况下,只有当线程池中的工作线程数大于corePoolSize时,keepAliveTime才会起作用,当工作线程数大于corePoolSize,此时如果工作线程空闲超过这个时间就会终止。但是如果allowCoreThreadTimeOut=true,工作线程数小于corePoolSize,也会根据keepAliveTime进行超时。
线程池状态
从成员变量中可以看到ThreadPoolExecutor线程池有5个状态,分别是:
- RUNNING:可以接受新的任务,也可以处理阻塞队列里的任务
- SHUTDOWN:不接受新的任务,但是可以处理阻塞队列里的任务
- STOP:不接受新的任务,不处理阻塞队列里的任务,中断正在处理的任务
- TIDYING:过渡状态,也就是说所有的任务都执行完了,当前线程池已经没有有效的线程,这个时候线程池的状态将会TIDYING,并且将要调用terminated方法
- TERMINATED:终止状态。terminated方法调用完成以后的状态
线程池状态机如下图:
内部类Worker
Worker类封装了线程池工作线程,即一个Worker实例代表一个工作线程。同时Worker类继承于AQS,实现了独占锁,一个Worker实例也是一个锁。同时Worker类实现了Runnable接口,重写了run()方法,于是一个Worker实例可以用于线程中执行。
// java.util.concurrent.ThreadPoolExecutor.Worker
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. */
final Thread thread;
/** Initial task to run. Possibly null. */
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) {
// 使用ThreadFactory构造Thread,这个构造的Thread内部的Runnable就是本身(传入的参数是this),也就是Worker。
// 所以得到Worker的thread并start的时候,会执行Worker的run方法,也就是执行ThreadPoolExecutor的runWorker方法
// 把AQS状态位state设置成-1,这样任何线程都不能得到Worker的锁,除非调用了unlock方法。
// 这个unlock方法会在runWorker方法中一开始就调用,这是为了确保Worker构造出来之后,没有任何线程能够得到它的锁,
// 除非调用了runWorker之后,其他线程才能获得Worker的锁
setState(-1);
this.firstTask = firstTask;
// 使用ThreadFactory构造Thread,这个构造的Thread内部的Runnable就是本身(传入的参数是this),也就是Worker。
// 所以得到Worker的thread并start的时候,会执行Worker的run方法,也就是执行ThreadPoolExecutor的runWorker方法
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker */
public void run() {
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;
}
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) {
}
}
}
}
因为在tryAcquire中获取锁时,是通过CAS操作,将state值从0修改成1成功的话,可以获取到独占锁,所以在Worker构造函数中将state值设为-1,可以让所有线程都无法获取到Worker的锁,直到runWorker方法被调用,这时才会释放锁(w.unlock()会调用tryRelease函数,在tryRelease函数中会设置state值为0),此时其他线程可以获取到Worker的锁。
注意:在Worker中实现的独占锁是不可重入的,用于判断工作线程是否空闲。
任务执行
在使用ThreadPoolExecutor类时,向线程池提交任务可以使用execute或submit方法,在基类AbstractExecutorService可以看到,实际上submit方法里面最终调用的还是execute()方法,所以execute方法就是执行任务的方法。
execute方法内部分3个步骤进行处理。
- 如果当前工作线程数量小于corePoolSize,直接创建一个新的工作线程执行任务,会调用addWorker方法。
- 如果当前工作线程数量大于等于corePoolSize,并且线程池状态是RUNNING,尝试将任务放到任务队列里,如果队列未满,则添加队列成功。否则执行第3步。添加队列成功之后,还需要再做一次验证(因为添加队列成功之后可能存在另外一个线程关闭了线程池,即调用了shutdown()方法,或者上次加入到工作线程集合中的线程已死),此时线程池如果不在RUNNING状态,把刚刚添加到任务队列的任务remove掉,执行拒绝策略,即调用reject()方法,否则查看工作线程数量,如果工作线程数量为0,则新建一个工作线程去任务队列获取任务执行。
- 如果添加任务队列失败,说明任务队列已满,会调用addWorker方法尝试创建一个新的工作线程,但是core参数值为 false,即非核心工作线程。去任务队列获取任务并执行,如果新建非核心工作线程失败,执行拒绝策略,即调用reject方法。
execute方法源码如下
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 1.工作线程数量小于corePoolSize,直接创建新工作线程执行任务
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 2.工作线程数量大于等于corePoolSize,workQueue未满,将任务添加到任务队列
if (isRunning(c) && workQueue.offer(command)) {
// 再次校验状态
int recheck = ctl.get();
// 2.1 如果此时为非运行状态,将从队列删除任务,并执行拒绝策略
if (! isRunning(recheck) && remove(command))
reject(command);
// 2.2 如果工作线程数为0,则新建一个工作线程
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 3.工作线程数量大于等于corePoolSize,workQueue已满,尝试创建新工作线程,创建失败执行拒绝策略
// 失败两种情况(判断逻辑在addWorker):
// 1.非RUNNING状态拒绝新的任务,在addWorker中会因为rs>=SHUTDOWN && firstTask != null 返回false
// 2.在addWorker中会因为 wc >= CAPACITY || wc >= maximumPoolSize)返回false
else if (!addWorker(command, false))
reject(command);
}
创建工作线程
当需要创建新工作线程的时候,就会调用addWorker方法,其主要功能为检查线程池的状态,判断是否满足创建新的工作线程的条件,如果满足则创建一个Worker实例作为新的工作线程来执行任务。
// firstTask表示需要执行的任务。
// boolean类型的core参数为true表示使用corePoolSize来限制工作线程数量,为false表示使用使用maximumPoolSize来限制工作线程数量
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
// 获取到ctl的值
int c = ctl.get();
// 计数出线程池的状态
int rs = runStateOf(c);
// 将这个判断转换成 rs >= SHUTDOWN && (rs != SHUTDOWN || firstTask != null || workQueue.isEmpty)。
// 将rs >= SHUTDOWN 定义为[condition_1],(rs != SHUTDOWN || firstTask != null || workQueue.isEmpty) 定义为[condition_2]
// 所以判断条件简化为:condition_1 && condition_2
// condition_1 为 true的条件很简单,即线程池为非RUNNING状态
// condition_2 为 true只要 rs != SHUTDOWN || firstTask != null || workQueue.isEmpty()其中一个为true即可
// 所以返回false有三种情况:
// (1)线程池是STOP、TIDYING或TERMINATED中的任意一种状态(rs != SHUTDOWN)
// (2)线程池是SHUTDOWN状态,且firstTask != null;
// (3)线程池是SHUTDOWN状态,firstTask == null,且阻塞队列为空;
// 总结:线程池为STOP、TIDYING或TERMINATED状态之一时,拒绝执行任务;线程池是SHUTDOWN状态时,拒绝执行新添加任务,但是如果阻塞队列非空,继续执行阻塞队列中的任务
if (rs >= SHUTDOWN && !(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()))
return false;
for (;;) {
//线程池中工作线程的数量
int wc = workerCountOf(c);
//线程池工作线程数超过最大容量或者超过设定corePoolSize(core==true)或maximumPoolSize(core==false)
// 创建工作线程失败
if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//cas操作将线程池中工作线程数量+1,成功的话跳出循环
if (compareAndIncrementWorkerCount(c))
break retry;
//重新检查状态
c = ctl.get();
//如果状态改变了,重新循环操作
if (runStateOf(c) != rs)
continue retry;
}
}
// 走到这一步说明cas操作成功了,线程池线程数量+1
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
int rs = runStateOf(ctl.get());
//如果线程池是running状态<第一种情况> 或者 线程池在SHUTDOWN状态并且不是新增加任务<第二种情况>
//<第二种情况>一般是线程池状态为SHUTDOWN,但是任务队列非空,所有增加一个工作线程执行队列中已有的任务;
// 同样说明,线程池状态为SHUTDOWN时不接受新任务
if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) {
// 如果工作线程已经启动,抛异常
if (t.isAlive())
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
// 启动线程,这里的t是Worker中的thread属性,所以相当于就是调用了Worker的run方法
t.start();
workerStarted = true;
}
}
} finally {
if (!workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
由于Worker中的线程创建时传入的Runnable是Worker本身(this),Worker中的线程start的时候,调用Worker本身run方法,而run方法调用外部类ThreadPoolExecutor的runWorker方法。
注意:addWorker方法中使用到break retry;
和 continue retry;
这就是Java的GoTo语句,虽然很多书上都不建议使用这样的语句,但是在这里还是说一下上面两个goto语句的区别。break retry;
是跳出循环,不会再进入到retry下面的for循环中,continue retry;
是重新循环,程序调到retry又会进入到for循环中。
运行工作线程
runWorker中刚进入时,如果新建任务(task != null),则先执行新建任务,执行完成后,从任务队列中获取任务并执行,获取不到任务则进入回收线程逻辑。
// java.util.concurrent.ThreadPoolExecutor#runWorker
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
// 这个就是在Worker构造函数说到的runWorker的unlock
w.unlock();
boolean completedAbruptly = true;
try {
// 一直循环,如果从队列中获取任务返回null,则退出循环,收回线程
while (task != null || (task = getTask()) != null) {
w.lock();
// 执行任务前,做一些检查
// 1. 如果线程池已经处于STOP状态并且当前线程没有被中断,中断线程
// 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 {
// 任务真正开始执行
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(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
从任务队列获取任务
getTask()就是从任务队列中获取任务,源码如下:
// java.util.concurrent.ThreadPoolExecutor#getTask
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// (1) 线程池处于SHUTDOWN状态并且任务队列为空,工作线程数减一,并且返回null
// (2) 线程池处于STOP状态,工作线程数减一,并且返回null
// 返会null意味着会执行processWorkerExit方法进行工作线程回收
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// timed的意思就是是否对当前工作线程加时间限制,有两种情况
// (1) allowCoreThreadTimeOut为true时
// (2) 工作线程数大于corePoolSize时
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 以下几种情况进行线程回收(return null,会执行processWorkerExit方法进行工作线程回收)
// (1) 工作线程数大于工作线程最大数
// (2) workQueue.poll获取任务超时
if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
// CAS执行失败,重新循环
continue;
}
try {
Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
总结一下Worker被回收的四种情况:
(1) 线程池处于SHUTDOWN状态并且任务队列为空,工作线程数减一,返回null
(2) 线程池处于STOP状态,工作线程数减一,返回null
(3) 工作线程数大于工作线程最大数,返回null
(4) workQueue.poll获取任务超时,返回null
工作线程回收
如果getTask返回的是null,那说明任务队列已经没有任务并且当前调用getTask的Worker需要被回收,那么会调用processWorkerExit方法进行回收。worker回收时需要检查线程池中的数量必须满足最小条件,当核心线程没有设置超时时,线程数量必须至少保持为corePoolSize;设置了超时的话,线程池中线程数量可以位0,但是如果阻塞队列不为空,则至少有一个工作线程。
//java.util.concurrent.ThreadPoolExecutor#processWorkerExit
private void processWorkerExit(Worker w, boolean completedAbruptly) {
// 如果Worker没有正常结束流程调用processWorkerExit方法,worker数量减一。
// 如果是正常结束的话,在getTask方法里worker数量已经减一了
if (completedAbruptly)
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//记录线程池完成的任务数量
completedTaskCount += w.completedTasks;
//从线程池的worker集合中删除需要回收的worker
workers.remove(w);
} finally {
mainLock.unlock();
}
//尝试结束线程池
tryTerminate();
int c = ctl.get();
//如果线程池还处于RUNNING或者SHUTDOWN状态
if (runStateLessThan(c, STOP)) {
// worker是正常结束流程
if (!completedAbruptly) {
//如果设置了allowCoreThreadTimeOut==true,即核心线程也设置了超时,所以线程池中线程数量可以为0,
//否则,线程池中线程的数量至少保持在设定的基本大小corePoolSize
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
//当任务队列不为空时,至少要有一个线程
if (min == 0 && !workQueue.isEmpty())
min = 1;
//如果目前线程池中的线程数量满足最小条件,则可以直接返回
if (workerCountOf(c) >= min)
return; // replacement not needed
}
// worker非正常结束流程
// 新开一个Worker代替原先的Worker
// 新开一个Worker需要满足以下3个条件中的任意一个:
// 1. 用户执行的任务发生了异常
// 2. Worker数量比线程池基本大小要小
// 3. 任务队列不空但是没有任何Worker在工作
// 三个条件会在tryTerminate进行判断,相当是如果满足上面三个条件中的任意一个tryTerminate流程会返回
addWorker(null, false);
}
}
尝试结束线程池
在processWorkerExit函数中回收工作线程的时候,以及在addWorkerFailed函数中处理添加工作线程失败的时候,都会调用tryTerminate尝试结束线程池。
// java.util.concurrent.ThreadPoolExecutor#tryTerminate
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) {
// 中断闲置Worker,直到回收全部的Worker。
// 这里没有那么暴力,只中断一个,中断之后退出方法,而在runWorker方法中检查到中断,会调用processWorkerExit
// 然后还是会调用tryTerminate方法,如果还有闲置线程,那么继续中断
interruptIdleWorkers(ONLY_ONE);
return;
}
// 走到这里说明worker已经全部回收了,并且线程池已经不在运行,任务队列已经没有任务。
// 可以准备结束线程池了
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
terminated();
} finally {
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
}
}
关闭线程池
shutdown方法,关闭线程池,调用shutdown关闭之后线程池状态变为SHUTDOWN,线程池会继续处理阻塞队列里的任务,但是新的任务不会被接受。
//java.util.concurrent.ThreadPoolExecutor#shutdown
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 把线程池状态更新到SHUTDOWN
advanceRunState(SHUTDOWN);
// 中断闲置的Worker,interruptIdleWorkers在调用onlyOne=false
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
停止线程池
调用shutdownNow方法关闭线程池后,线程池状态变为STOP,此时线程池不会接受新的任务,也不会继续处理阻任务列中的任务,同时会立即终止所有有效工作线程的运行。
//java.util.concurrent.ThreadPoolExecutor#shutdownNow
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;
}
拒绝策略
当线程池的任务队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略
- ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
- ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
- ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
- ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
// 由调用线程处理该任务
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
// 丢弃任务并抛出RejectedExecutionException异常
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an {@code AbortPolicy}.
*/
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
// 也是丢弃任务,但是不抛出异常
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
//丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
彩蛋
学习完线程池的源码后,是不是会觉得线程池也没多少东西,核心的就是:一个工作线程类Worker,一个任务队列workerQueue,一些CAS操作线程状态和工作线程数,一个维护线程池状态和线程池有效工作线程数量ctl,哈哈!终于引出了我想说的问题了,不知道大家有没有想过线程池为什么要用一个变量ctl来保存线程池状态和线程池有效工作线程数量两个内容,为什么不用两个变量分别保存线程池状态和线程池有效工作线程数量,用量两个变量这样在线程池状态和工作线程数的操作上是不是会简单很多。那为什么会这么设计呢?我个人觉得主要是考虑并发情况下线程安全性问题。
按照正常的思路,减少变量的数量无非就是节约空间,但是,在这里明显不是为了省空间了,因为就算用两个值分开表示状态和工作线程数量,也就8个字节而已。把这两个值存放到一个AtomicInteger变量中,在进行读写操作时,可以很好的保证这两个值始终是统一的,而且不需要额外的同步机制,如果用两个变量保存,那么在同时操作线程池状态和工作线程数时,就需要额外同步机制,否则会出现数据不统一。然后在选择使用一个变量保存两个值的情况,开发者巧妙的使用位运算,这样可以提高计算速度,节省空间。