ThreadPoolExecutor中通过isRunning方法检查线程池状态:
//原子操作类,防止并发,前三位存储线程池状态,剩余位数表示线程池中线程数量
AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//判断线程池是否是运行状态
private static boolean isRunning(int c) {
return c < SHUTDOWN;
}
// 代表线程池正在运行
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;
execute
通过调用ExecutorServic的submit和execute方法,可以添加一个任务到线程池中。至于这两个方法的区别在于submit方法添加的任务会返回任务的执行结果,execute方法没有返回结果。在submit方法中仍然是调用execute方法来添加任务。
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// workerCountOf(c)用于计算线程数量,并判断是否小于核心线程数量
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//执行到这里说明当前线程数>=核心线程数,接着判断线程池状态,若是Running则将任务入队
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//线程池状态异常,则将之前添加的任务删除,并执行饱和策略
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
//线程池中没有线程,则创建一个非核心线程执行任务
addWorker(null, false);
}
else if (!addWorker(command, false))
//执行到这里,说明任务添加队列失败,且线程>=核心线程,那么执行饱和策略
reject(command);
}
小结一下execute方法的执行步骤:
- 首先判断正在运行的线程数是否小于核心线程数,少于的话会调用addWorker方法来创建一个核心线程来执行当前任务。
- 当前正在执行的线程数>=核心线程数,若当前线程池状态为正在运行,则执行入队操作,将任务添加到队列中。为了防止并发,再次判断线程池状态,如果线程池状态异常,则删除刚才入队的任务,并执行饱和策略。如果线程池状态为正在运行且线程数为0,则创建一个临时线程,也就是非核心线程执行任务。
- 执行到这里,表明当前线程数量>=核心线程数,且入队失败,会再次调用addWorker方法创建一个非核心线程来执行任务,创建失败则执行饱和策略。
addWorker
通过对execute方法的分析,我们可以看到具体的线程创建与复用机制应该在addWorker方法中。
addWorker方法中接收两个参数,firstTask表示接收的第一个任务,core表示是否创建核心线程。
private boolean addWorker(Runnable firstTask, boolean core) {
......
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
//worker对象的作用类似于具体的执行单元
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
//获得锁,注意mainLock为非公平锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
int rs = runStateOf(ctl.get());
//只有当线程池处于Running状态,才会继续往下执行
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
//如果Worker对象中的线程已经在执行了,抛出一个异常给他
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) {
t.start();
workerStarted = true;
}
}
} finally {
//线程启动失败的话,删除当前worker对象
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
上述省略了部分代码,首先会根据任务创建一个Worker对象,然后获得Worker内部的Thread对象,后面会调用Thread的start方法来执行线程。线程的创建和任务的执行并没有具体的细节体现,可以预料到在Worker内部,那么具体的细节进入Worker的内部看一看:
private final class Worker extends AbstractQueuedSynchronizer implements Runnable{
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
}
Worker类实现了Runnable接口和AQS锁,在Worker的构造函数中会保存传递见来的任务,然后调用线程工厂创建一个线程。我们注意到Worker类实现了Runnable接口,并且在创建线程时将当前Worker对象传递给Thread。所以当调用hread的start方法启动线程时,回调的是Worker的run方法。
// run方法中委托runWorker方法来继续执行
public void run() {
runWorker(this);
}
//
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
//获取当前Worker保存的任务
Runnable task = w.firstTask;
w.firstTask = null; // firstTask置null,方便gc,因为Worker对象存活时间较长
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
//如果线程池时Stop状态,那么要确保当前线程为中断状态
//如果当前线程处于Running或SHUTDOWN状态,确保当前线程没有设置中断状态
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对象的run方法中会调用runWorker方法,在runWorker方法中存在一个while循环,首先执行Worker中保存的任务(也就是创建Worker对象时的第一次任务),若该任务为null,则会调用ThreadPoolExecutor的getTask方法从阻塞队列中获取任务执行,任务执行完毕会循环调用getTask方法获取任务执行,所以说线程池中复用的并不是Thread,只是在循环的获取任务的Runnable对象并执行。
getTask
线程的复用原理在于getTask方法中任务的获取,根据线程池状态、队列大小和是否被超时策略影响来决定是否返回null值,结束线程。
private Runnable getTask() {
//队列中获取任务超时时,会返回null,并重置timedOut = true
boolean timedOut = false;
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
//检查线程池状态,分为两种情况,返回null
//1. 线程池状态为SHUTDOWN,且队列为空
//2. 线程池状态为STOP
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// timed表示超时策略,allowCoreThreadTimeOut表示核心线程是否允许超时设置
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//1. 如果当前线程数>最大线程数,或者当前Worker被超时策略控制
//2. 如果线程池中存在线程,或者队列为null,
//同时满足以上1,2条件的话,返回null,线程被结束
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
//poll方法从队列中取出一个任务,取不到则挂起当前线程keepAliveTime时间,超出指定时间仍取不到,返回null
//take方法从队列中取出一个任务取不到时,会一直挂起
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
//任务不为null,返回
if (r != null)
return r;
//超时,还没取到,继续执行for循环,最终会执行到上述的if方法内
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
小结一下getTask方法中的具体做了什么:
- 检查线程池状态,如果线程池状态为SHUTDOWN,且队列为空,或者程池状态为STOP,则返回null值结束线程。
- 控制线程的超时策略时,可以设置allowCoreThreadTimeOut=true,修改核心线程受超时策略的控制,默认为false。
- 如果当前Worker被超时策略控制且队列为null,会返回null,结束当前线程。上述if中wc > 1是因为,要加上当前Worker对象所在的线程。
- 核心线程在未设置allowCoreThreadTimeOut=true的前提下,会指定take方法获取任务,超时等待。其他线程调用poll方法获取任务,超时指定时间,会走到步骤3中,最终返回null,结束线程。
总结
- 当调用线程池的execute方法时,首先会判断当前正在执行的线程数是否小于核心线程树,若小于,则调用addWorker方法创建一个Worker对象执行当前任务,若是核心线程数已满,则会将任务加入阻塞队列,加入阻塞队列失败的情况下会创建非核心线程来执行任务,也是调用addWorker方法,如果还是失败的话执行饱和策略。
- addWorker方法中会委托Worker对象的runWorker方法来执行具体的任务,内部执行一个while循环调用getTask方法获取任务执行,当获取任务为null时,结束线程。
- getTask方法中维护一个for循环,内部根据线程池状态,队列大小和当前线程是否会被执行超时策略,来获取任务,核心线程默认不执行超时策略,会一直等待获取任务,可以通过设置allowCoreThreadTimeOut,来强行设置超时策略,非核心线程在超时获取任务为null,且队列为null的情况下,会返回null,结束线程。