ThreadPoolExecutor的主要是管理ThreadFactory创建的线程,Executor提交的任务。看一下ThreadPoolExecutor的继承图:
由上图可以看到,ThreadPoolExecutor是Executor和ExecutorService的一个实现。这个类也是整个Executor实现的核心,它的实现主要分为三个主要的部分:
1、核心线程池,管理了用于执行task的线程。
2、任务等待队列,当任务无法获得线程执行时,用于保存等待的task。
3、拒绝策略,当等待队列已满,无法存储新的task时,则采用提供的拒绝策略拒绝新的task。
这三个部分在ThreadPoolExecutor的构造方法中就有体现,构造方法如下:
public ThreadPoolExecutor( int corePoolSize, //核心线程数量
int maximumPoolSize, //最大线程数量
long keepAliveTime, //超时时间
TimeUnit unit, //超时时间的单位
BlockingQueue<Runnable> workQueue, //waiting task queue,用于保存等待的task
ThreadFactory threadFactory, //线程工厂是用户自定义的
RejectedExecutionHandler handler //拒绝策略
) {}
一、核心线程池的实现
ThreadPoolExecutor中提供了一种机制,该机制维护了一定数量的线程(corePoolSize)。对于新进入的task,这个机制可以根据实际情况作出四种反应:1、使用现有的线程执行task ;2、放入等待队列(workQueue); 3、建一个创新的程执线行task;4、直接拒绝。这个机制就是线程池。
在ThreadPoolExecutor中,executor方法负责将task提交给ThreadPoolExecutor。接下来看一下executor方法的实现,了解一下线程机制是如何体现的。 executor方法源代码如下:
public void execute(Runnable command) {
/**
* 源代码阅读说明:
* 1、这里的command就是task;因为这里使用了命令模式,所以用command命名;执行task,task被包装成了一个work。
* 2、isRunning检查当前ThreadPoolExecutor是否是运行状态
* 3、workerCountOf会根据ThreadPoolExecutor的状态值获得当前正的正在执行的和等待的work数量。
* 这里先不用深究isRunning和workerCounterOf方法的实现。
* 4、addWork方法会新增一个线程,返回true表示新增成功,返回false表示新增失败
*/
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//判断当前正在运行的线程是否达到核心线程数
//如果小于核心线程数,直接addWork
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//运行的线程已经达到核心线程数
//workQueue.offer(command)将task放入队列中
//offer添加成功则放回true,反之是false
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//放入队列成功,再次isRunning, 如果已经是非运行状态,则移除当前的task
//调用reject拒绝当前任务
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false); //再次调用addWork,注意addWork的参数
}
else if (!addWorker(command, false)) //放入队列失败,再次调用addWork
reject(command);
}
上面的代码的执行流程大致如下:
1、execute方法接收到一个task(也就是command);
2、判断当前正在运行的线程数是否大于corePoolSize,如果小于corePoolSize,则直接新增线程运行task。
3、 当前正在运行的线程数大于corePoolSize,会将task放入workQueue。如果放入成功,则task就会等待执行。
4、如果放入workQueue失败,则会再次尝试addWork(注意,再次addWork时,第二个参数是false),如果再次addWork失败,则拒绝该task。
这个执行流程基本就是线程池的运行方式。这里的addWork方法返回true表示新增线程成功,反之则失败。接下来看一下addWork代码的实现,了解addWork何时返回true,何时返回false。代码如下:
private boolean addWorker(Runnable firstTask, boolean core) {
/**
* 源代码阅读说明:
* 1、runStateOf返回ThreadPoolExecutor的运行状态。状态值分别是:
* RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED
* 2、boolean变量core表示是否使用核心线程
* 3、CAPACITY常量表示ThreadPoolExecutor所支持的最大线程数量,
* 这个值与corePoolSize和maxPoolSize都没有关系,是JDK本身做的限制
*/
retry: //循环退出标签
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
//检查当前状态是否是SHUTDOWN,SHUTDOWN状态将不会再接收task
//这就是为什么调用shutdown方法后,将不会再接收task的原因
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY || //判断当前运行的线程数是否已经超过最大线程数
wc >= (core ? corePoolSize : maximumPoolSize))//这里要特别留意,下文会详细说明
return false;
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
}
}
//addWorker余下的部分代码是用于新增线程和启动线程,下文进行说明
………………
}
wc >= (core ? corePoolSize : maximumPoolSize)说明:
首先,这个判断的用途是判断当前的work数量是否大于corePoolSize或者maximumPoolSize。具体根据谁来判断,是有boolean值来决定的。
其次,在executor方法中,第一次判断当前执行的work是否大于corePoolSize,如果小于,则会进行addWork(command,true);这里的true就是core的值,也就决定了这个判断的值。因为ThreadPoolExecutor是多线程运行的,所以在这里又进行了第二次校验。
最后, 在executor方法中,再次进行addWork时,其调用形式是addWork(command,false);core的值是false,也就是说第二次addWork创建的线程之后,线程的数量threadCount是:corePoolSize < threadCount < maxmumPoolSize
接下来是addWork的第二部分代码:
private boolean addWorker(Runnable firstTask, boolean core) {
………………
boolean workerStarted = false; //是否已经启动
boolean workerAdded = false; //是否已经添加进workers
Worker w = null;
try {
w = new Worker(firstTask); //创建一个worker
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w); //将线程放入线程队列,workers是一个hasSet<Runnable>集合
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true; //修改添加的状态值
}
} finally {
mainLock.unlock();
}
if (workerAdded) { //如果已经添加成功,则启动线程,并修改状态值
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted) //没有启动的线程,则进行移除,addWorkerFailed方法会移除新创建的worker
addWorkerFailed(w);
}
return workerStarted;
}
到这里,整个execute的提交过程基本已经完成了,但是仍然存在几个疑问:
1、workQueue等待队列中的task如何被执行呢?
2、限制了线程的数量,意味着就需要重用这些线程,那这些线程又是如何被重用的呢?
关于这两个问题的实现是在Worker这个类中,也就是new Worker(firstTask)创建出来的对象中。加下来看一下Worker的shixian
private final class Worker extends AbstractQueuedSynchronizer implements Runnable{
/**
* 源代码阅读说明:
* 1、Worker类实现了AbstractQueuedSynchronizer接口,这里并不讨论AQS的实现
* 2、这里的代码只截取了一部分,详细的代码可以阅读原文
*/
final Thread thread;
Runnable firstTask;
volatile long completedTasks;
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this); //创建线程
}
public void run() {
//在addWork中有一行t.start()启动线程的代
//当线程启动时,runWorker方法将会被执行
runWorker(this);
}
…………
}
final void runWorker(Worker w) { //runWork方法的实现
Thread wt = Thread.currentThread();
Runnable task = w.firstTask; //取出worker中的task
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) { //这个循环判断就是用于获取workerQueue的task
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(); //调用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);
}
}
【task != null || (task = getTask()) != null】这个判断中,首先会判断task是否为Null,如果为null,就会调用getTask从workQueue中获取task。反之,则会执行当前的task。当task本执行完之后,会在finally中将task置为null,紧接着下一次循环就会从workQueue中取任务,依次循环 ,知道workQueue中的task被执行完成。这里的取task操作是多线程执行,所以workQueue必须是线程安全的。
最后,在ThreadPoolExecutor的构造函数中还有一个参数keepAliveTime,线程的存活时间。keepAliveTime需要结合allowCoreThreadTimeOut的值一起使用。allowCoreThreadTimeOut表示是否允许核心线程超时,true表示允许核心线程超时,false表示核心线程不会超时失效。
当allowCoreThreadTimeOut的值为true时,所有在ThreadPoolExecutor中的线程在达到超时时间keepAliveTime时,都会失效;反之,则只有corePoolSize< newThreadCount <maxmumPoolSize中的newThreadCount部分的线程会失效。
也就是说,当ThreadPoolExecutor维护的线程数量超过corePoolSize的值,多余部分的线程是一定会超时失效的;而对于小于corePoolSize的线程是否会失效,则取决于allowCoreThreadTimeOut的值。关于超时线程失效的代码实现在getTask方法中,代码如下:
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) { //死循环,不断获取workQueue中的worker
int c = ctl.get();
int rs = runStateOf(c);
// ThreadPoolExecutor已经停止了,则所有的线程都应该退出了
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null; //线程返回退出
}
int wc = workerCountOf(c);
//当allowCoreThreadTimeOut为true时,或者当前执行的worker超过corePoolSize时
//需要检查超时设置,因为:
//wc > corePoolSize表示有超出的多余线程,多余的线程是有超时限制的
//allowCoreThreadTimeOut表示所有的线程都有可能超时
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//timedOut:获取worker超时
//这个校验判断,如果成立,则线程就会返回退出;反之,则会一直循环等待。
//通过循环等待的方式,保证线程的有效
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(); //取出队列中的worker
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
二、任务等待队列
任务等待队列通常是一个BlockQueue阻塞队列:
1、当运行的线程数少于corePoolSize时,对于新提交的task,会新增一个线程去执行task。
2、当运行的线程数大于corePoolSize,小于maxmumPoolSize时,ThreadPoolExecutor会将新的task放入队列中。
3、当新的task放入队列失败时,需要判断当前运行的线程数量是否超过maxmumPoolSize,如果大于maxmumPoolSize,则拒绝该task;如果小于maxmumPoolSize,则创建一个新的线程。
通常情况下,等待队列有如下三种策略:
1、直接传递,示例:SynchronousQueue。在这种策略下,队列不会持有任何的task,而是将task直接传递给线程执行。如果没有空闲线程,则创建线程。在执行有内部依赖(task之间相互有依赖)的请求时,这种策略可以避免死机或者死锁。此外,由于队列不持有task,因此ThreadPoolExecutor需要一个无界的maxmumPoolSize,以此来保证task不会被拒绝。此种策略不适合耗时的任务。
2、无界队列,示例:LinkedBlockingQueue。这种策略是创建一个可以无限扩展的队列,因为ThreadPoolExecutor会优先将task放入队列中,在超时corePoolSize的情况下。所以,采用无界队列就意味着maxmumPoolSize将不再起作用。由于,task都被放入了队列中,也就要求各个task之间不能够相互依赖,否则将会出现死锁。
3、有界队列,示例:ArrayBlockingQueue。算是上面两种策略的一种折中。可以通过maxmumPoolSize来限制系统资源的使用,以保证系统资源不会被过度消耗。队列的size和maxmumPoolSize的设置将会很重要;如果queueSize大,maxmumPoolSize小,会限制了系统的吞吐量;反之,queueSize小,maxmumPoolSize大;则可能导致资源耗尽。
三、拒绝策略
当task的数量超过ThreadPoolExecutor所允许的值时,ThreadPoolExecutor就会拒绝该task。ThreadPoolExecutor有四种拒绝的方式:
1、抛出RejectedExecutionException,策略类:ThreadPoolExecutor.AbortPolicy;
2、由调用者本身的线程去执行改task,策略类:ThreadPoolExecutor.CallerRunsPolicy;
3、丢弃当前的task,策略类:ThreadPoolExecutor.DiscardPolicy;
4、丢弃队列头部的task,然后将当前的task加入队列中,策略类:ThreadPoolExecutor.DiscardOldestPolicy。
拒绝策略的源代码非常简单,一看就懂,这里就不赘述了。