最近将ThreadPoolExecutor源码又读了一遍,将以前没有弄的太懂的地方给弄懂了点。所以写下这篇博客记录一下自己的理解。
在看源码之前得知道线程池中几个参数的作用和线程池大体的工作原理,这样才能更好的理解源码。
一.线程池的构造方法
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize:线程池的核心线程数,当线程池中的工作线程数小于核心线程数的时候,只要向线程池指派任务,线程池就会创建工作线程。
maximumPoolSize:线程池最大工作线程数,当线程池中的工作线程达到最大数的时候,即使再向线程池指派任务,线程池不会创建工作线程。
keepAliveTime:当线程池的工作线程数大于核心线程数的时候,多余的核心线程数的部分线程可以保持keepAliveTime的空闲时间,当keepAliveTime时间内还没有获取到任务,这些线程后就会被回收。
unit:保持空闲时间的时间单位。
workQueue:任务队列,当线程池里面核心线程都在工作的时候,再向线程池指派任务,线程池会将任务放入任务队列里,工作线程在执行完任务后会再向任务队列里取出任务来执行。
threadFactory:创建执行任务的工作线程的线程工厂。
handler:拒绝任务加入线程池的策越,当线程池里的线程已经达到最大数后,再向线程池里加派任务时,线程池会决绝执行这些任务,handler就是具体执行拒绝的对象。
线程池的大体工作思路
1.当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
2.当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
3.当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
4.当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理
5.当线程池中超过corePoolSize数的线程,空闲时间达到keepAliveTime时,关闭空闲线程
6.当设置allowCoreThreadTimeOut(true)时,线程池中核心线程空闲时间达到keepAliveTime也将关闭
你看到这里其实对线程池大体的工作原理就清仓了,如果你想研究源码可以继续往下看。
二. 线程池方法源码解读
1.参数解读
/*
*可以将这个参数看成是一个三十二位的二进制数,
*其中前三位表示线程池的状态,
*后二十九位表示线程池中工作线程的数量
*/
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
//CAPACITY值为:00011111111111111111111111111111
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
//RUNNING状态表示线程池可以接受任务正常工作
private static final int RUNNING = -1 << COUNT_BITS;
//SHUTDOWN状态表示线程池不接受任务,但如果阻塞队列中还有任务,会将阻塞队列中的任务执行完
private static final int SHUTDOWN = 0 << COUNT_BITS;
//STOP状态表示线程池不接受任务,也不会执行阻塞队列中的任务,即使阻塞队列中还存在任务
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
//获取线程池的状态,一般会用形参rs表示,在后面rs参数一般表示线程池状态
private static int runStateOf(int c) { return c & ~CAPACITY; }
//获取线程池工作线程的数量,一般用形参wc表示
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
线程池的5种状态:Running、ShutDown、Stop、Tidying、Terminated。
线程池各个状态切换框架图:
1、RUNNING
(1) 状态说明:线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。
(02) 状态切换:线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0!
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
- 1
2、 SHUTDOWN
(1) 状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。
(2) 状态切换:调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。
3、STOP
(1) 状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
(2) 状态切换:调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP。
4、TIDYING
(1) 状态说明:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
(2) 状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。
当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。
5、 TERMINATED
(1) 状态说明:线程池彻底终止,就变成TERMINATED状态。
(2) 状态切换:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。
--------------------- 作者:一只逗比的程序猿 来源:CSDN 原文:https://blog.csdn.net/l_kanglin/article/details/57411851?utm_source=copy 版权声明:本文为博主原创文章,转载请附上博文链接!
2.execute方法
execute方法是线程池执行任务的入口,execute方法的入参是必须要实现了Runnable接口的实现类
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
/*
*获取线程池中工作线程的数量和核心线程数做比较
*如果小于就调用addWorker方法,
*
*如果失败了会重新获取工作线程数量和线程池状态,成功就直接返回
**/
if (workerCountOf(c) < corePoolSize) {
/*
* 此时调用addWorker方法就是创建一个新的工作线程来执行这个任务
* 向addWorker方法传入参数true,会在addWorker方法里面继续将工作线程数量和核心线程数做比较。
*/
if (addWorker(command, true))
return;
c = ctl.get();
}
/*
* 程序运行到这里的前提条件有两个
* 1.工作线程数量大于核心线程数量
* 2.调用addWorker方法失败
* 如果线程池是running状态,而且任务加入阻塞队列成功,执行if了里面的代码
* if里面的代码其实是对加入到阻塞队列的任务的条件的一个后续判断,这句话比较绕
* 意思是将任务放到阻塞队列了后,我还要判断是否应该将这个队列放到阻塞队列中
* 这里为什么后续还要加判断我也不是很懂。干嘛不加在前面呢?
*/
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
/*
* 重新判断线程池的工作状态,如果不是running状态,就将任务从队列中移除
* 移除后调用线程池的拒绝策越,默认的是抛出异常
*/
if (! isRunning(recheck) && remove(command))
reject(command);
/*
* 执行到这的前提条件:线程池的状态是running
* 如果将任务加入到了队列中,却发现线程池中已经没有工作线程了怎么办?
* 我刚开始对线程池中没有工作线程还存在疑问,当allowCoreThreadTimeout设置成true的时候
* 核心线程有可能在空闲时间超时后被回收,所以线程池存在工作线程数为0的情况
* ,或者核心线程数设置为0也会出现这个情况
*/
else if (workerCountOf(recheck) == 0)
/*
* 传false将会在addWorker方法中判断线程池的工作线程数量和最大线程数量做比较
* 传一个空的任务,开启一个工作线程,但这个工作线程会发现当前的任务是空,然后会去队列中取任务
* 这样就避免了线程池的状态是running,而且队列中还有任务,但线程池却不执行队列中的任务
*/
addWorker(null, false);
}
/*
* 程序执行到这的前提条件有两个:
* 1.线程池的工作状态不是running
* 2.任务加入到队列失败了
* 如果是第一种情况线程池的工作状态不是running了,那调用addWorker方法也会返回false,
* 就会调用拒绝执行任务的策越
* 如果是第二种情况,加入队列失败,这说明队列已经满了,那调用addWorker方法,参数传fasle
* 表示会将线程池的工作线程数量和最大线程数量比较,如果小于就会创建新的工作线程来执行这个任务
*/
else if (!addWorker(command, false))
reject(command);
}
execute方法比较绕,不是很直观,因为它的判断条件有点多,我感觉写法比较怪异。在execute方法中没有看到要执行任务的地方,在这个方法里只是做了条件判断,如果满足
条件的话,就交给addWorker处理,并且根据判断条件的不同,传给addWorker方法的参数也不同。总结一下execute方法的执行逻辑
1.当工作线程数量小于核心线程数量的时候,会将任务交给addWorker方法,addWorker方法会创建新的线程来处理这个任务
2.当工作线程数量大于和核心线程数量并且线程池的工作状态是running的时候,会将任务放入到阻塞队列中
3.当阻塞队列已经满了,会将任务交给addWorker方法处理
4.当交给addWorker方法处理失败或是线程池的状态不是running的时候,会调用线程池的拒绝策越。
execute方法其实是线程池工作的一个大体思路,具体细节我们可以看看addWorker方法。
3.addWorker方法
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
/*
* 可以将if条件里面的判断条件这样看:
* rs >= SHUTDOWN &&(rs != SHUTDOWN ||firstTask != null || workQueue.isEmpty())
* 所以在这里能进if,让addWorker返回false的情况有这样几种
* 1.当线程池的状态是stop
* 2.当线程池的状态是shutdown的话,firstTask不为空
* 3.当线程池的状态是shutdown的话,队列是空的
* 以上三种情况返回false
*/
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函数增加线程池工作线程数,如果成功就直接跳出这两层循环
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 {
final ReentrantLock mainLock = this.mainLock;
//Worker是线程池的一个内部类,其实完成任务和从队列中取任务都是在Worker中完成的
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
mainLock.lock();
try {
int c = ctl.get();
int rs = runStateOf(c);
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();
}
//当将任务放到任务队列(不同于阻塞队列)成功后,启动工作线程,执行firstTask任务
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
可以看到任务的启动是在addWorker方法是生成一个新的工作线程来开启任务。Worker就将工作线程和任务封装到了自己内部,我们可以将Worker看成就是一个工作线程,至于Worker是如何执行任务和从阻塞队列中取任务,那就是Worker的事了
4.内部类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).
*/
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
// ...
/** 工作线程 */
final Thread thread;
/**任务 */
Runnable firstTask;
/**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
*/
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker. */
public void run() {
runWorker(this);
}
// ...
}
这里我只贴出了 Worker类中和本次分析有关的代码, 其他代码就省略掉了. Worker类内部还包含一个线程对象 thread 和一个 Runnable对象 firstTask. 而这个线程对象的创建过程见35行, 将Worker对象自身作为这个线程对象 thread的 Runnable参数传递给 thread的构造方法. 那么我们就可以知道, 如果要运行该线程, 也就是执行 thread.start(), 那么实际上就是要执行 thread所在的 Worker类中的 run()方法, 而从第40行又可以知道, Worker类中的 run()方法又是调用 runWorker(this); 这个方法的, 并将 thread所在的 Worker对象作为这个 runWorker()方法的参数. 简单来说就是:
启动一个 Worker对象中包含的线程 thread, 就相当于要执行 runWorker()方法, 并将该 Worker对象作为该方法的参数.
5.runWorker方法
/*
* 这个方法是执行任务,当前任务执行完后会向队列里面取任务,
* 如果队列里也没任务了,就会将这个工作线程从工作线程队列中移除
* 所以线程池几个线程执行多个任务就在这里体现了,而不是
* 一个线程执行一个任务
*/
final void runWorker(Worker w) {
//工作线程
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//任务执行完后,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
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
//如果没有继承ThreadPoolExecutor实现这个方法,这个方法是没有执行动作的
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 {
//如果没有继承ThreadPoolExecutor实现这个方法,这个方法是没有执行动作的
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
//走到这里说明队列里的任务都已经被执行完,移除工作线程
processWorkerExit(w, completedAbruptly);
}
}
6.getTask方法
import java.util.concurrent.TimeUnit;
/*
* 从阻塞队列中取任务,取任务有三种情况发生
* 1.渠道任务并返回任务
* 2.没有取到任务,返回null,这个工作线程被回收
* 3.没有取到任务,阻塞在向阻塞队列取任务这里
* 第三点就是线程池中的空闲任务是如何存在的
*/
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
//当线程池的工作状态是stop,就减少工作线程数,返回null
//当线程池的工作状态是SHUTDOWN并且队列是空的时候,就减少工作线程数,返回null
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
boolean timed; // Are workers subject to culling?
for (;;) {
int wc = workerCountOf(c);
/*
* 当允许核心线程超时后被收回或者是工作线程数大于核心线程数
* 这两种情况下都是一定要回收工作线程的
*/
timed = allowCoreThreadTimeOut || wc > corePoolSize;
/*
* 第一次执行这个语句的时候,是一定会进if里面,跳出里面这层循环的,因为初始化的timedOut=false
* 当不进入这个循环,说明工作线程超时了,工作线程超时一般会返回null;
*/
if (wc <= maximumPoolSize && ! (timedOut && timed))
break;
/*
* 说明工作线程超时了,工作线程超时一般会返回null;
* 减少工作线程数量
*/
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 {
/*
* timed为true的时候,当工作线程取任务超时就会返回,返回后会被回收
* 当timed为false的时候,说明当前工作线程不需要被回收,所以就可以在向阻塞队列取任务的时候被阻塞
* 这里就提现了线程池中空闲的线程是如何存在的
*/
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
/*
* 走到这里,r一定为空,最后会进入到第40行的if里面,还是返回null
* 当返回null,这个工作线程就会被回收
*
*/
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
7.processWorkerExit方法
/**
* 减少工作线程数,将工作线程从工作线程队列中移除
*/
private void processWorkerExit(Worker w, boolean completedAbruptly) {
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();
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);
}
}
通过源码,不仅知道了线程池的工作原理,而且知道了工作线程是如何执行任务的,当工作线程空闲的时候,线程池是如何处理的。
总结:
1.线程池的调用过程
Worker对象里面有两个属性,一个是任务,一个是工作线程,Worker对象实行了Runnable接口,重写了run方法,在创建工作线程的时候会将Worker对象作为任务传给工作线程Thread,我们启动工作线程的时候,其实是在执行Worker这个任务,但是在Worker这个任务的run方法中其实是执行我们传给线程池的任务,在Worker的run方法中执行的是我们传入任务的run方法,直接调用run方法。
2.线程池为什么能重复利用线程,其实是利用工作线程在for循环里面不断调用getTask方法获取阻塞队列里面的任务来执行的。当如果是核心线程取任务的时候,会利用队列的take方法,take方法如果取不到任务的话,就会一直被阻塞,如果是非核心工作线程,就会利用poll方法,poll方法可以设置超时时间,如果在超时时间内取不到任务的话,就会返回false,返回工作线程就会被回收。
3.减少工作线程,就会将工作线程从工作线程队列里面移除。
参考文章:http://blog.csdn.net/clevergump/article/details/50688008