不过,我倒是对线程池是如何回收工作线程比较感兴趣,所以简单分析了一下,加深对线程池的理解吧。工作者线程就是执行run()方法,然后内部调用runWorker()。
一、拆解runWorker
1 拆解ThreadPoolExecutor之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 {
while (task != null || (task = getTask()) != null) {
w.lock();
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);
}
}
Worker哪来的?我们在addWorker的时候new的,来看下Worker构造器:
private final class Worker extends AbstractQueuedSynchronizer implements Runnable{
Worker(Runnable firstTask) {
// 设置AQS是state为-1,主要目的是为了不让中断。
setState(-1);
// 设置任务
this.firstTask = firstTask;
// 创建线程
this.thread = getThreadFactory().newThread(this);
}
}
state是AQS的变量,-1啥意思?没啥意思,很简单,就是说我这个任务不可以被中断。那为啥要这么设置?废话,你都没开始执行呢,你只是new一个任务出来,线程都没启动,怎么可能允许中断呢?恍然大悟…!
那再看unlock干了啥?
public void unlock() {
release(1);
}
调用AQS的release方法,给state释放1,也就是说unlock后state变成了0,通俗点就是:我现在任务得到了执行,我要让他允许中断了,怎么允许?当然是state=0,这都是AQS的知识!所以为啥Worker要继承AQS?因为他巧妙的运用了AQS的中断。
2 while循环拿任务
while (task != null || (task = getTask()) != null) {
...
}
Runnable task = w.firstTask;
task一上来就是w.firstTask,也就是说我们在addWorker方法里包装在Worker里的任务,第一次肯定不是null,所以会执行while循环体,执行完后再finally里给task弄成了null。所以这个条件仅在第一次执行的时候为true,因为每次finally都会把task弄成null。
task = getTask()
:从队列里取任务,取出来后赋值给task,如果队列里有任务就执行循环体,执行完成后会给任务从队列里remove掉,如果getTask获取不到任务则会阻塞,因为底层是BlockingQueue。
3 中断判断(线程池的状态引入)
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;
/*
* 线程池状态是大于等于STOP的话(执行了shutdownNow),并且你没有被中断过的话(!wt.isInterrupted()),则让线程中断;
* 也就是说,线程池状态都是大于等于STOP的了,那么设置中断标记位,告诉这个线程说:你小子别找事啊,你赶紧给我回来,你被开除(中断)了。
*/
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
private static boolean runStateAtLeast(int c, int s) {
return c >= s;
}
4 beforeExecute
beforeExecute(wt, task);
在线程任务开始执行前做一些处理,可以自定义实现方法。模板方法。需要注意的地方是他只被try finally包起来了,没有catch,也就是说异常会被吞,即使报错,如果用户不做catch捕获的话,那么将不会影响线程下面的工作。这很关键,对下面的completedAbruptly有决定性的作用。
5 任务执行
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);
}
开始执行任务,也就是你提交到线程池里的任务,且捕获异常。
6 afterExecute
afterExecute(task, thrown);
在线程任务执行之后做一些处理,可以自定义实现方法。模板方法。需要注意的地方是他只被try finally包起来了,没有catch,也就是说异常会被吞,即使报错,如果用户不做catch捕获的话,那么将不会影响线程下面的工作。这很关键,对下面的completedAbruptly有决定性的作用。
7 finally
好奇怪啊,为啥afterExecute那里的解释我说他被try finally包起来了?哪被包起来了?我说错了?看下面代码:
try {
beforeExecute(wt, task);
try {
task.run();
} catch (RuntimeException x) {
...
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
好家伙,afterExecute确实被包起来了,被这段代码包起来了:
try {
afterExecute(task, thrown);
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
首先可以发现也没有被catch捕获。其次就是一些辅助工作,比如task弄成null来辅助最外层的while循环,完成的任务数+1,解锁的工作。剩余没讲的代码还有最后一段,那就是:processWorkerExit,再说这个方法之前,必须看下前面提到两次的completedAbruptly是个什么鬼。
8 completedAbruptly
// 默认是true
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
...
try {
beforeExecute(wt, task);
try {
task.run();
} catch (Throwable x) {
...
} finally {
afterExecute(task, thrown);
}
} finally {
...
}
}
// 这里设置成false
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
很简单,如果任务正常得到执行,没有任何异常,他就是false,如果中途发生了异常,那就是true。具体含义是是否发生了中断,而且这个中断还是用户自己中断的,因为比如beforeExecute、afterExecute啥的都是重写才可能发生异常。
9 processWorkerExit
private void processWorkerExit(Worker w, boolean completedAbruptly) {
// 如果任务执行过程中发生了报错,则CAS的方式把任务数-1。ctl的低29位。
if (completedAbruptly)
// CAS的方式将任务数-1。
decrementWorkerCount();
// 上锁 保证将任务移除队列的线程安全。
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
// 将任务移除队列,因为任务已经执行完了嘛
workers.remove(w);
} finally {
// 解锁
mainLock.unlock();
}
// 钩子函数,下一篇幅讲解
tryTerminate();
/*
* 下面这段代码的含义是如果线程池是RUNNING或者SHUTDOWN状态的话,
* 且任务顺利完成(completedAbruptly=false)的话,那么判断是否设置了允许核心线程超时
* 如果允许核心线程超时,且任务队列不等于空的话,那么开启一个线程来执行任务。
* 一言以蔽之:如果线程池是RUNNING或者SHUTDOWN状态的话,且任务队列不是空,那么至少保证线程池中有一个线程在执行任务
*/
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;
}
addWorker(null, false);
}
}
10 总结
1、先unlock调用AQS的release方法,让任务可中断。(因为任务已经开始执行了,可以中断了)
2、while循环拿任务,没任务就阻塞,采取的BlockingQueue的阻塞api
3、中断判断,线程池状态是大于等于STOP的话(执行了shutdownNow),就让线程中断
4、线程执行前会先执行beforeExecute,可重写
5、真正的任务执行
6、线程执行前会先执行afterExecute,可重写
7、执行完成后将任务从workers里remove掉
8、如果线程池是RUNNING或者SHUTDOWN状态的话,且任务队列不是空,那么至少保证线程池中有一个线程在执行任务
二、拆解getTask
private Runnable getTask() {
boolean timedOut = false;
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
1 timed
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
如果设置了核心线程允许超时的话,则timed=true,反之false。
如果线程池中活跃线程数大于核心线程数,则timed=true,反之false。
timed干啥用的?不是傻子的话都该看出来了,释放线程用的,也就是说非核心线程(大于核心线程数了)要被释放,允许核心线程超时的话也要被释放。
2 释放线程
Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();
如果timed是true,那么走workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)也就是设置获取任务的超时时间,到时间后还没获取到任务的话则会timeOut=true。
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
timeOut=true的话,getTask有个判断会让其跳出循环,线程生命周期也自然而然的随之结束。
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
反之如果timed是false的话,那么会执行workQueue.take();不带超时时间的,则一直阻塞等待有结果返回。
其实一句话就概括了:从队列中获取任务,如果timed是true的话,则调用阻塞队列的poll方法阻塞一段时间获取任务,这段时间没任务的话,则超时设置timeOut=true,结束生命周期。否则调用take()方法一直阻塞等待任务到来,也就是核心线程为什么能一直存活的原因。
三、总结
1、runWorker方法就是执行任务的,执行前后可以实现方法自定义干一些事情。
2、任务执行完了后就会从workers这个HashSet里移除
3、核心线程一直存活的方法是调用阻塞队列的take(),不带超时时间,一直阻塞等待任务到来。
4、非核心线程超时后就结束的方法是调用阻塞队列的poll(long time),让其这段时间内没任务的话就结束方法生命周期,自然而然的线程跟着销毁。