一、前言
在《阿里巴巴开发手册》中有对针对线程池的使用有着明确的要求,原文如下:
【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样 的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors 返回的线程池对象的弊端如下:
1)FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2)CachedThreadPool 和 ScheduledThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
那么为什么开发手册会选择ThreadPoolExecutor呢?今天通过这篇文章可以为大家揭开ThreadPoolExecutor这层神秘的面纱。
二、FixedThreadPool 等是怎么实现的?
FixedSizeThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
SingleThreadPool
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
CachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
ScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
/** ScheduledThreadPoolExecutor */
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
/** super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue()); */
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
由上可知,FixedThreadPool 等都是基于ThreadPoolExecutor来实现的,那么FixedThreadPool的底层实现原理到底是什么呢?接下来我们来看看ThreadPoolExecutor源码
三、ThreadPoolExecutor源码解析
核心构造方法
//核心的构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
//先对于参数的数值进行判断,即是否小于0或者最大线程数是否小于核心线程数
//若不符合规范则抛出异常
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
//然后对任务队列以及线程工厂和拒绝策略进行空指针判断,若为空则抛出异常
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
//若都符合规范则对于ThreadPoolExecutor中的变量进行赋值
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
构造方法的参数含义
1.corePoolSize:线程池核心线程数大小。
2.maximumPoolSize:线程池线程数最大值,达到最大值后线程池不会再增加线程执行任务。
3.keepAliveTime:线程池中超过corePoolSize数目的空闲线程最大存活时间,allowCoreThreadTimeOut为 true的核心线程有效时间。
4.unit:时间单位。
5.workQueue:阻塞任务队列,用于保存任务以及为工作线程提供待执行的任务。
6.threadFactory:线程工厂,线程生成器。
7.handler:当提交任务数超过maxmumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来处理。
8.ThreadPoolExecutor内部有实现4个拒绝策略,默认为AbortPolicy策略:
(1) CallerRunsPolicy:由调用execute方法提交任务的线程来执行这个任务。
(2) AbortPolicy:抛出异常RejectedExecutionException拒绝提交任务,默认的拒绝策略。
(3) DiscardPolicy:直接抛弃任务,不做任何处理。
(4) DiscardOldestPolicy:去除任务队列中的第一个任务,重新提交。
拒绝策略如何实现?
ThreadPoolExecutor是在其内部有四个内部类,分别对应四种拒绝策略,他们均是继承RejectedExecutionHandler接口,实现rejectedExecution方法,以此来实现拒绝策略的具体处理过程。
① CallerRunsPolicy内部类
//CallerRunsPolicy拒绝策略:让调用该任务的线程来执行该任务
public static class CallerRunsPolicy implements RejectedExecutionHandler {
//构造方法
public CallerRunsPolicy() { }
//在调用者的线程中执行任务r,除非执行器已经关闭,在这种情况下任务将被丢弃。
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
//先判断线程池是否被关闭
if (!e.isShutdown()) {
//如果还没被关闭,则运行此任务
r.run();
}
}
}
② AbortPolicy内部类
//AbortPolicy拒绝策略:拒绝任务并抛出RejectedExecutionException异常
public static class AbortPolicy implements RejectedExecutionHandler {
//构造方法
public AbortPolicy() { }
//拒绝任务并抛出RejectedExecutionException异常
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
③ DiscardPolicy内部类
//DiscardPolicy拒绝策略:拒绝任务但不抛出异常
public static class DiscardPolicy implements RejectedExecutionHandler {
//构造方法
public DiscardPolicy() { }
//什么也不做,直接丢弃
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
④ DiscardOldestPolicy内部类
//DiscardOldestPolicy拒绝策略:丢弃同步队列最前面的那个任务,然后重新尝试执行任务(调用execute方法)
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
//构造方法
public DiscardOldestPolicy() { }
//丢弃队列中最前面的任务,然后重新尝试执行任务(调用execute方法)
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
//先判断当前线程池是否为关闭状态
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
ThreadPoolExecutor的变量含义
//AtomicInteger(ctl)是一个32位的整数,为了将状态和数量放在一起。
//所以高3位用于表示表示状态,低29位表示数量。
//初始化线程数
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//Integer.SIZE = 32,COUNT_BITS = 32 - 3 = 29
private static final int COUNT_BITS = Integer.SIZE - 3;
//CAPACITY用32位表示:0001 1111 1111 1111 1111 1111 1111 1111
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
//RUNNING用32位表示:1110 0000 0000 0000 0000 0000 0000 0000
//能接受新任务,队列中的任务可继续运行
private static final int RUNNING = -1 << COUNT_BITS;
//SHUTDOWN用32位表示:0000 0000 0000 0000 0000 0000 0000 0000
//不再接受新任务,队列中的任务仍可继续执行
private static final int SHUTDOWN = 0 << COUNT_BITS;
//STOP用32位表示:0010 0000 0000 0000 0000 0000 0000 0000
//不再接受新任务,不再执行队列中的任务,中断所有执行中的任务(发中断消息)
private static final int STOP = 1 << COUNT_BITS;
//TIDYING用32位表示:0100 0000 0000 0000 0000 0000 0000 0000
//所有任务均已终止,workerCount的值为0,转到TIDYING状态的线程即将要执行terminated()钩子方法.
private static final int TIDYING = 2 << COUNT_BITS;
//TERMINATED用32位表示:0110 0000 0000 0000 0000 0000 0000 0000
//terminated()方法执行结束
private static final int TERMINATED = 3 << COUNT_BITS;
//用于保存任务并将任务传递给工作线程的队列。
private final BlockingQueue<Runnable> workQueue;
//锁
private final ReentrantLock mainLock = new ReentrantLock();
//设置包含池中的所有工作线程。只有在持有主锁时才能访问。
private final HashSet<Worker> workers = new HashSet<Worker>();
//支持等待终止的等待条件
private final Condition termination = mainLock.newCondition();
//返回池中同时存在的最大线程数。
private int largestPoolSize;
//完成任务的计数器。仅在工作线程终止时更新。仅在主锁下访问。
private long completedTaskCount;
//线程工厂,新建线程池时带入,用来创建新的线程
private volatile ThreadFactory threadFactory;
//拒绝策略,默认是AbortPolicy,会在构造方法中赋值的
//AbortPolicy :拒绝任务并抛出RejectedExecutionException异常;
//DiscardPolicy:拒绝任务但不抛出异常;
//DiscardOldestPolicy:丢弃队列中最前面的任务,然后重新尝试执行任务(调用execute方法);
//CallerRunsPolicy:调用线程执行该任务;
private volatile RejectedExecutionHandler handler;
//当线程数量超过corePoolSize时,多出来的线程数量的在空闲时的存活时间
private volatile long keepAliveTime;
// 如果为false(默认值),核心线程即使在空闲时也保持活动状态。
// 如果是,核心线程使用keepAliveTime来超时等待工作。
private volatile boolean allowCoreThreadTimeOut;
//线程池中最大的核心线程的数量,核心线程即为即使处于等待状态也不会被销毁的线程
private volatile int corePoolSize;
//线程池允许的最大线程数
private volatile int maximumPoolSize;
//默认的拒绝策略:拒绝任务并抛出RejectedExecutionException异常
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
ThreadPoolExecutor线程池的几个状态
状态 | 描述 | 二进制 |
---|---|---|
RUNNING | 线程池正常运行,可以接受新的任务并处理队 列中的任务 | 1110 0000 0000 0000 0000 0000 0000 00 00 |
SHUTDOWN | 关闭状态,不再接受新的任务,但是会执行队 列中的任务。在线程池处于 RUNNING 状态 时,调用 shutdown()方法会使线程池进入到 该状态。(finalize() 方法在执行过程中也会 调用shutdown()方法进入该状态) | 0000 0000 0000 0000 0000 0000 0000 0000 |
STOP | 不再接受新任务,不处理队列中的任务,中断 进行中的任务。在线程池处于 RUNNING 或 SHUTDOWN 状态时,调用 shutdownNow() 方法会使线程池进入到该状 态 | 0010 0000 0000 0000 0000 0000 0000 0000 |
TIDYING | 所有任务已经终止,workerCount为0,线程 状态转换到TIDYING,线程池进入该状态后 会调用 terminated() 方法进入 TERMINATED 状态 | 0100 0000 0000 0000 0000 0000 0000 0000 |
TERMINATED | terminate()函数执行完成后进入该状态 | 0110 0000 0000 0000 0000 0000 0000 0000 |
线程池状态之间的转换
/**
* 各状态之间可能的转变有以下几种:
* 1.RUNNING -> SHUTDOWN:
* 调用了shutdown方法,线程池实现了finalize方法。
* 在finalize内调用了shutdown方法。
* 因此shutdown可能是在finalize中被隐式调用的
* 2.(RUNNING or SHUTDOWN) -> STOP
* 调用了shutdownNow方法
* 3.SHUTDOWN -> TIDYING
* 当队列和线程池均为空的时候
* 4.STOP -> TIDYING
* 当线程池为空的时候
* 5.TIDYING -> TERMINATED
* terminated()方法调用完毕
*/
四、线程池的相关流程
execute执行的过程
// 在将来的某个时候执行给定的任务。任务可以在新线程中执行,也可以在现有的池线程中执行。
// 如果任务无法提交执行,可能是因为这个执行器已经关闭,也可能是因为它的容量已经达到,
// 那么该任务将由当前拒绝策略处理。
public void execute(Runnable command) {
//空指针判断检查
if (command == null)
throw new NullPointerException();
//获取当前线程池信息,状态+线程数量
int c = ctl.get();
//workerCountOf是获取当前线程池的线程数量
//判断当前线程池的线程数量是否小于corePoolSize
if (workerCountOf(c) < corePoolSize) {
//如果当前线程池的线程数量小于corePoolSize
//就将当前任务线程添加进线程池然后返回
if (addWorker(command, true))
return;
//如果addWorker方法没有将此线程成功的加入线程池
//则再次获取当前线程池的状态和线程数量
c = ctl.get();
}
// isRunning表示先判断当前线程池是否在运行状态
// 如果线程池处于运行状态,则将任务存放在任务队列
if (isRunning(c) && workQueue.offer(command)) {
//再次获取线程池的信息
int recheck = ctl.get();
//先检查当前线程池是否处于运行状态
//如果不处于运行状态,则将刚刚workQueue.offer的任务从任务队列中移除
if (! isRunning(recheck) && remove(command))
//如果线程池不处于运行状态,并且移除成功
//则按照拒绝策略来对此任务进行处理
reject(command);
//检查当前线程池的线程数量是否为0
else if (workerCountOf(recheck) == 0)
根据情况是否要将新起一个线程
addWorker(null, false);
}
//如果线程池不处于运行状态或者添加任务进任务队列失败了
//就新起一个线程
else if (!addWorker(command, false))
//如果新起线程失败,则对任务按照拒绝策略进行处理
reject(command);
}
addWorker的执行过程
//线程池尝试新增一个线程,并执行任务
//core表示:如果真使用corePoolSize作为边界,则使用maximumPoolSize。
private boolean addWorker(Runnable firstTask, boolean core) {
//注意
retry:
for (;;) {
//获取当前线程池的信息
int c = ctl.get();
//获取当前线程池的运行状态
int rs = runStateOf(c);
//如果当前线程池的运行状态为SHUTDOWN/STOP/TIDYING/TERMINATED
//并且 当前线程池的状态为SHUTDOWN以及任务为空以及任务队列为空
//则直接返回false
//一句话,就是判断线程池是否处于RUNNING状态且任务不为空
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
//获取当前的线程数
int wc = workerCountOf(c);
//若线程池超过最大容量,或大于设定的容量
//corePoolSize与maximumPoolSize均为传入的参数
//那么直接返回false
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//那么采用cas机制,将线程池计数器扩增1,跳出标签
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
}
}
//上面若将线程池计数器加1了
//这里就要对线程池扩增了
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
//创建一个线程实例
w = new Worker(firstTask);
//获取线程
final Thread t = w.thread;
if (t != null) {
//获取锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//获取线程池状态
int rs = runStateOf(ctl.get());
//若线程池状态为running状态
//或为SHUTDOWN并且传入的线程为空
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) {
//执行该线程任务
t.start();
workerStarted = true;
}
}
} finally {
//若未启动成功
if (! workerStarted)
//回滚当前新起线程操作
//移除当前新增失败的线程
//将线程池计数器减1
//尝试中断线程池或者中断当前线程
addWorkerFailed(w);
}
return workerStarted;
}
runWorker执行的过程
//执行线程任务
final void runWorker(Worker w) {
//获取当前线程
Thread wt = Thread.currentThread();
//获取当前线程该执行的任务
Runnable task = w.firstTask;
//先将当前线程代表的任务变为null
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//循环从缓冲队列内拿出task,直到task内为空
while (task != null || (task = getTask()) != null) {
//获取锁
w.lock();
//当前的线程池状态为STOP/TIDYING/TERMINATED
if ((runStateAtLeast(ctl.get(), STOP) ||
//或 现在已经中断且当前线程池的状态为STOP/TIDYING/TERMINATED
(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);
}
}
getTask执行过程
//从线程池内获取一个task,成功返回一个Runnable的task,失败返回null。
private Runnable getTask() {
//超时标志,轮询标志位,表明轮询是否超时
boolean timedOut = false;
for (;;) {
//获取当前线程池信息
int c = ctl.get();
//获取当前线程池的运行状态
int rs = runStateOf(c);
//如果线程池的状态为SHUTDOWN/STOP/TIDYING/TERMINATED
//并且 线程池状态为STOP/TIDYING/TERMINATED 或者 任务队列为空
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
//则减少线程数量
decrementWorkerCount();
return null;
}
//获取当前的线程数
int wc = workerCountOf(c);
//标志位 线程池允许核心线程超时都不销毁 或者 当前线程数大于核心线程数
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//若当前线程数大于最大线程数 或者 timed 且 timedOut均为true(超时)
if ((wc > maximumPoolSize || (timed && timedOut))
//并且当前线程数大于1 或者任务队列为空
&& (wc > 1 || workQueue.isEmpty())) {
//则线程池中线程数成功减1,返回空
//否则继续下一轮自旋
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
//根据标志位从缓冲队列内轮询或者直接拿一个task
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
//抛出异常
workQueue.take();
//若拿到了则成功返回
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
tryTerminate执行过程
//方法作用:进行状态转换以及中断一个线程
final void tryTerminate() {
for (;;) {
//获取当前线程池的信息
int c = ctl.get();
//若当前线程池为运行状态
//或者线程池的状态大于TIDYING,即线程池的状态为TIDYING/TERMINATED
//或线程池的状态为SHUTDOWN和任务队列不为空
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
//则//返回,此时线程池状态不用做任何变更
return;
//若当前线程池的线程数量不为0
if (workerCountOf(c) != 0) { // Eligible to terminate
// 给线程池的首个线程发中断消息
// 进行尝试中断其运行
interruptIdleWorkers(ONLY_ONE);
return;
}
//获得全局锁
//设置线程池的状态为
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//将线程池的状态变为TIDYING,ctlOf是将参数1和参数2进行或操作
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
terminated();
} finally {
//将线程状态设置为TERMINATED,ctlOf是将参数1和参数2进行或操作
ctl.set(ctlOf(TERMINATED, 0));
//给其他线程发消息,告知该线程池即将关闭
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
五、总结
总的来说,ThreadPoolExecutor还是花费了相当多的时间来进行解读,而且还有一些看不懂的。只单纯看博客是无法深刻了解ThreadPoolExecutor的,只有自己亲自去看了他的源码,才能有所体会。