Java 1.7 ThreadPoolExecutor源代码解析
相比1.6,1.7有些变化:
1、 添加了一个TIDYING状态。这个状态是介于STOP和TERMINATED之间的。假设运行完terminated钩子函数后状态就变成TERMINATED了;
2、 内部类Worker继承了AQS类作为一个独享锁,在执行每一个任务前会获取自己的锁。
3、 runState和poolSize两个字段被合并成一个原子字段ctl了,不再使用mainLock保护了。
一、成员变量介绍
public class ThreadPoolExecutor extends AbstractExecutorService {
/**
* ctl字段事实上表示两个含义:runState和workerCount(近似1.6中的poolSize)
* int类型。高3位表示runState,低29位表示workerCount。 眼下这个版本号也就限
* 制了线程个数不会超过2^29-1。
* RUNNING: 能接受新的任务且能处理队列里的请求
* SHUTDOWN: 不能接受新的任务可是能处理队列里的请求
* STOP: 不能接受新的任务、不能处理队列里的请求。workers会被interrupt
* TIDYING: 全部的线程都已经terminated了,正准备调用terminated()方法
* TERMININATED: terminated()方法已经调用结束了
*
* RUNNING->SHUTDOWN: 调用shutdown方法
* (RUNNING/SHUTDOWN)>STOP: 调用shutdownNow方法
* SHUTDOWN->TIDYING: 当workers和queue都空的时候
* STOP->TIDYING: 当workers为空的时候
* TIDYING->TERMINATED: 当terminated方法调用结束的时候。
* awaitTermination()直到状态为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; // 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; // 取ctl的高三位。获取runState(执行状态)
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 取ctl的低29位,获取workerCount(worker的数量)
private static int workerCountOf(int c) { return c & CAPACITY; }
// 把runState和workerCount合并成ctl,上面两个函数的反操作
private static int ctlOf(int rs, int wc) { return rs | wc; }
二、execute函数
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* 三步走:
* 1. 假设RUNNING的线程数目小于corePoolSize,直接调用addWorker方法
* 启动一个新线程。addWorker函数会检查runState和workerCount。假设不
* 须要新建一个thread就会返回false了
*
* 2. 假设任务被成功的放入了workQueue,我们仍然须要做个double-check
* 由于调用完isRunning(c)后池中的线程可能都退出了或者线程池被shut
* down了。又一次检查状态看是要remove掉新来的任务还是创建一个新线程来执
* 行(假设没有活动的线程了)
*
* 3. 假设放入workQueue失败了,我们尝试创建一个新worker。 假设失败了,
* 说明线程池被关闭了或者饱和了(超过最大值了)。就直接拒了。
*
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
// addWorker有可能会失败,失败后又一次获取状态并继续往下走
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 假设isRunning(c)&&workQueue.offer中间并发发生了shutdown,须要remove
// 掉刚放入workQueue的command任务。注意:此时假设有一个worker刚运行完一个task
// 然后从workQueue获取下一个task时,这里的remove就会失败了。
if (! isRunning(recheck) && remove(command))
reject(command);
// 假设是RUNNING状态可是没有可工作的线程。须要直接new一个
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
execute函数大体思路和1.6一致,就三种情况:
① 当前线程池中线程数目小于corePoolSize,直接new一个thread。
② 当先线程池数据大于corePoolSize,则放入workQueue中;
③ 假设workQueue满了且线程池中线程数小于maximumPoolSize,则new一个thread。
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c); // 假设被shutdown了。一般就直接返回false。可是须要排除一个特例情况:当线程池状
// 态是shutdown。但workQueue不空且workers空了,会调用addWorker(null,false)
// 方法创建一个线程处理workQueue里的任务,这时不能直接返回false。
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false; for (;;) {
int wc = workerCountOf(c);
// 假设当前workers数目大于CAPACITY或者大于用户设置了。直接返回false
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
// 假设不过workerCount变化了,那么继续内层的循环;假设连runState也变化了,
// 则要又一次继续外层的循环。 if (runStateOf(c) != rs)
continue retry;
}
} boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
final ReentrantLock mainLock = this.mainLock;
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int c = ctl.get();
int rs = runStateOf(c);
// 再次检查runState的状态,假设是RUNNING或者SHUTDOWN可是firstTask不空,则
// 把new出来的worker放入workers中。
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
// 创建worker成功后直接启动线程了
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
// 创建失败要做清理操作
addWorkerFailed(w);
}
return workerStarted;
}
addWorker函数尝试新建一个thread来执行传递给它的task。当线程池被STOP或SHUTDOWN或threadFactory返回null时或者OOM时。会返回false并做对应的清理。整个过程分为两步:1、尝试设置workerCount,成功了就到步骤2;2、尝试创建一个worker并增加到workers里。
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (w != null)
workers.remove(w);
decrementWorkerCount();
tryTerminate();
} finally {
mainLock.unlock();
}
}
addWorkerFailed函数做些清理操作:1、把创建的worker从workers中删除;2、把workerCount减1;3、检查能否够terminated线程池,防止这个worker的存在导致运行awaitTermination操作的client线程堵塞了。
final void tryTerminate() {
for (;;) {
int c = ctl.get();
// 假设是以下三种情况直接返回:
// 1.RUNNING状态; 2.runState>=TIDYING。说明有其它线程运行了tryTerminate操
// 作; 3.SHUTDOWN状态且workQueue不空
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
// 假设workerCount大于0。则中断一个空暇的worker,就返回了。为啥仅仅中断一个呢?
// 由于worker线程退出时也会调用tryTerminate方法(一个接一个的传播)
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
} final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 走到这里说明workers数量为0了,尝试把线程池状态改成TIDYING并调用terminated
// 函数->状态再设置成TERMINATED。 假设设置TIDYING失败,则继续循环。
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
terminated();
} finally {
// terminated函数抛异常也须要运行以下的操作。
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
tryTerminate函数尝试TERMINATED线程池(当a、SHUTDOWN且queue和pool都空;b、STOP且queue为空了)。假设workers不为0,则中断随意一个空暇的worker后直接返回。否则:首先,将线程池状态改成TIDYING;其次,调用用户的钩子函数terminated;最后,将状态设置成TERMINATED。
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
// 假设tryLock成功,就说明这个worker是空暇的。 if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
// 假设仅仅中断一个就break,仅仅有tryTerminate函数中使用到这样的情况。
break;
}
} finally {
mainLock.unlock();
}
}
interruptIdleWorkers函数依据onlyOne參数决定中断一个或全部空暇的workers(这些workers都堵塞在getTask方法中)。
三、shutdown函数
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 检查调用者是否有权限运行shutdown
checkShutdownAccess();
// 将线程池的状态改成SHUTDOWN
advanceRunState(SHUTDOWN);
// 中断全部空暇的workers
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
// 尝试终止线程池
tryTerminate();
}
shutdown函数就运行几步:把状态改成SHUTDOWN。中断全部空暇的workers,调用onShutdown钩子函数,最后调用tryTerminate尝试终止线程池。
private void advanceRunState(int targetState) {
for (;;) {
int c = ctl.get();
if (runStateAtLeast(c, targetState) ||
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
break;
}
}
advanceRunState函数将线程池的状态改成指定状态值。假设如今状态值比target值大就直接返回。targeState的值是SHUTDOWN或者STOP,不能是TIDYING或者TERMINATED(这两种状态须要调用tryTerminate函数设置)。
四、shutdownNow函数
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 检查调用者是否有权限运行关闭
checkShutdownAccess();
// 将线程池的状态改成STOP
advanceRunState(STOP);
// 和shutdown不同,这里中断全部的worker线程
interruptWorkers();
// 删除workQueue里的任务并返回任务列表
tasks = drainQueue();
} finally {
mainLock.unlock();
}
// 尝试终止线程池
tryTerminate();
return tasks;
}
shutdownNow函数会中断全部的worker线程,删除workQueue里的任务,最后尝试终止线程池并返回workQueue里的任务。
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 中断全部的worker线程
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
private List<Runnable> drainQueue() {
BlockingQueue<Runnable> q = workQueue;
List<Runnable> taskList = new ArrayList<Runnable>();
q.drainTo(taskList);
if (!q.isEmpty()) {
for (Runnable r : q.toArray(new Runnable[0])) {
if (q.remove(r))
taskList.add(r);
}
}
return taskList;
}
五、Worker内部类
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/** 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) {
// 初始值为-1,防止worker还没启动就被interrupt了;在start開始时会将状态改成0
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(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;
}
// 參数1没有意义,是独占锁
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
Worker类主要维护着中断的管理和其它操作(runWorker函数),继承了AQS类实现了一个不可重入的Lock。在获取到一个任务后,准备运行前首先要获取这个锁。
同一时候。在中断空暇的worker时也要先获取到这个锁。
public void run() {
runWorker(this);
}
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
// 有时我们不想从workQueue取第一个任务,直接运行刚提交的任务
Runnable task = w.firstTask;
w.firstTask = null;
// 把state设置成0,同意中断
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
// 进入循环了
while (task != null || (task = getTask()) != null) {
w.lock();
// 假设是STOP状态,须要保证线程是被中断了的;
// 假设不是须要清空中断状态,可是须要又一次检查下状态防止在清除
// 中断时发生了shutdownNow
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);
}
}
runWorker函数循环从workQueue里获取task并运行,可是须要注意下面几个问题:1.假设不想从workQueue里获取第一个任务运行,那就给worker.firstTask赋值。2、假设getTask获取的值为null,或者你的task里抛异常了,那循环就退出了,然后worker线程也就退出了。3、在运行任务前先要获取worker的锁,这里防止中断正在运行的线程。4、假设你的钩子函数beforeExecute函数抛异常了,那么你的任务就不会被运行了,worker线程也会退出。5、假设task.run方法抛出Runtime或Error异常,会原样抛出。假设是Throwable,则会包装成一个Error抛出,抛出异常前会运行afterExecute钩子函数。最后线程会退出。6、假设afterExecute钩子函数抛出异常。那么worker线程也会退出。
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out? retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c); // 假设SHUTDOWN且workQueue为空,或者STOP了。worker线程直接退出
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
// 是否要回收这个worker线程?
boolean timed; // Are workers subject to culling? for (;;) {
int wc = workerCountOf(c);
timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 假设还没有超时过(循环第一次运行到这里)直接break
if (wc <= maximumPoolSize && ! (timedOut && timed))
break;
// 否则。假设线程数大于最大限制或者已经超时过了说明这个worker线程要准备退出了
// 先设置workerCount-1,成功的话直接退出。否则,看下runState是否和rs一样,如
// 果一样就在内部循环,不一样就要到外部循环
if (compareAndDecrementWorkerCount(c))
return null;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
} try {
// 无限堵塞或超时堵塞
Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
// 没有获取到task肯定是超时了
timedOut = true;
} catch (InterruptedException retry) {
// 假设被中断了,不能算作超时
timedOut = false;
}
}
}
getTask函数是从workQueue里获取一个task,有两种策略(无限堵塞或者超时,详细要看client的配置)。
假设这个函数返回了null,那么worker线程就会退出了。退出的原因不外乎下面几种:
1. 当前线程池中worker数量大于maximumPoolSize了。
2. 线程池被STOP了(workQueue.poll/take时会捕获到InterruptedException异常);
3. 线程池被SHUTDOWN了且workQueue为空(workQueue.poll/take时会捕获到InterruptedException异常);
4. 获取task超时了(timedOut)&&(timed)。
private void processWorkerExit(Worker w, boolean completedAbruptly) {
// 用户的函数抛异常了,须要调整workerCount的值,由于worker线程准备退出了
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount(); // 做些统计操作(bookkeeping)
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
// 尝试终止线程池
tryTerminate(); int c = ctl.get();
// 假设是RUNNING或SHUTDOWN状态,要看下workQueue是否为空,
// 不能直接退出。 假设workQueue不空,至少要保留1或corePoolSize个
// 线程(看allowCoreThreadTimeOut配置)。少于这个数目,就须要通过
// addWorker(null,false)方法补充新的线程进来。 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);
}
}
processWorkerExit函数是在runWork循环退出后做的清理和bookkeeping(应该就是指completedTaskCount等变量的操作吧)操作。
completedAbruptly參数的含义是指用户的函数是否抛异常了(before/after/run等)。注意下函数最后会依据线程池的状态和配置决定是否新建一个worker线程。
Java 1.7 ThreadPoolExecutor源代码解析的更多相关文章
- Java 线程池(ThreadPoolExecutor)原理解析
在我们的开发中“池”的概念并不罕见,有数据库连接池.线程池.对象池.常量池等等.下面我们主要针对线程池来一步一步揭开线程池的面纱. 有关java线程技术文章还可以推荐阅读:<关于java多线程w ...
- Android Java 线程池 ThreadPoolExecutor源代码篇
线程池简单点就是任务队列+线程组成的. 接下来我们来简单的了解下ThreadPoolExecutor的源代码. 先看ThreadPoolExecutor的简单类图,对ThreadPoolExecuto ...
- Java 1.7 ReentrantReadWriteLock源代码解析
因为本人水平与表达能力有限,有错误的地方欢迎交流与指正. 1 简单介绍 可重入读写锁时基于AQS实现的,典型的用法如JDK1.7中的演示样例: class RWDictionary { private ...
- Java并发之ThreadPoolExecutor源码解析(二)
ThreadPoolExecutor ThreadPoolExecutor是ExecutorService的一种实现,可以用若干已经池化的线程执行被提交的任务.使用线程池可以帮助我们限定和整合程序资源 ...
- 知名互联网公司校招 Java 开发岗面试知识点解析
天之道,损有余而补不足,是故虚胜实,不足胜有余. 本文作者在一年之内参加过多场面试,应聘岗位均为 Java 开发方向.在不断的面试中,分类总结了 Java 开发岗位面试中的一些知识点. 主要包括以下几 ...
- Java开发岗面试知识点解析
本文作者参加过多场面试,应聘岗位均为 Java 开发方向.在不断的面试中,分类总结了 Java 开发岗位面试中的一些知识点. 主要包括以下几个部分: Java 基础知识点 Java 常见集合 高并发编 ...
- [ Java面试题 ]Java 开发岗面试知识点解析
如背景中介绍,作者在一年之内参加过多场面试,应聘岗位均为 Java 开发方向. 在不断的面试中,分类总结了 Java 开发岗位面试中的一些知识点. 主要包括以下几个部分: Java 基础知识点 Jav ...
- Arrays.sort源代码解析
Java Arrays.sort源代码解析 Java Arrays中提供了对所有类型的排序.其中主要分为Primitive(8种基本类型)和Object两大类. 基本类型:采用调优的快速排序: 对象类 ...
- Android EventBus源代码解析 带你深入理解EventBus
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40920453,本文出自:[张鸿洋的博客] 上一篇带大家初步了解了EventBus ...
随机推荐
- stm32开发 - 远离 Keil uVision, 回到 Visual Studio
学了8051单片机, 学了MSP430系列, 终于开始步入正轨, 开始学习 stm32(ARM Cortex-M3)系列微处理器~ 学51用Keil uVision开发环境, 提一下Keil uVis ...
- 豆瓣上9分以上的IT书籍-编程语言篇
我当要学习某些技术时,第一时间就是去找相关的书籍.而豆瓣读书是我主要的参考依据,主要是它的评分基本比较靠谱,对于技术书籍,一般来说评分在8分以上就是不错的书籍了,而达到9分就可以列入"必读& ...
- 【转】写给支持和反对《完全用Linux工作》的人们
早就有人问起我的学习情况,问我有没有找到理想的研究环境.我却总是弄一些小动物,要不就是好玩的内容在这上面.真是惭愧,因为一直觉得自己还没有什么发言权,一直觉得是不是自己搞错了.不过来了 Cornell ...
- Android系统版本与API级别对照表
对照表 API Level 最初Android版本 Linux内核版本 首次发布日期 后续Android版本 28 9 Unknown 2018-07-02(Beta 3) - 27 8.1 4.10 ...
- 带你开始进入NPM的世界之NPM包的开发
个人开发包的目录结构 ├── coverage //istanbul测试覆盖率生成的文件 ├── index.js //入口文件 ├── introduce.md //说明文件 ├── lib │ ...
- 五分钟读懂UML类图(转)
平时阅读一些远吗分析类文章或是设计应用架构时没少与UML类图打交道.实际上,UML类图中最常用到的元素五分钟就能掌握,下面赶紧来一起认识一下它吧: 一.类的属性的表示方式 在UML类图中,类使用包含类 ...
- Mac下 如何配置虚拟机软件Parallel Desktop--超详细
Mac下 如何配置虚拟机软件Pparallel Desktop--超详细 Mac 的双系统解决方案有两种,一种是使用Boot Camp分区安装独立的Windows,一种是通过安装Parallels D ...
- spring注解方式实现定时器
1.Spring的配置: <beans xmlns:task="http://www.springframework.org/schema/task" xsi:schemaL ...
- mysql数据导入遇到的timestamp类型问题
今天准备把最新的表导入自己以前的机子上做临时开发,在数据库导入的时候遇到一个问题:Incorrect table definition; there can be only one TIMESTAMP ...
- IOS 视频流
https://github.com/kolyvan/kxmovie demo 项目