线程池主要解决两个问题:
一 是 执行大量异步任务 时,线程池能够提供较好的性能,没当需要执行异步任务时 直接 new 一个线程来运行,而线程的创建 和 销毁是需要开销的。线程池里的线程是 可复用 的,不需要每次执行异步任务时都重新创建和销毁线程。
二 是 线程池提供了一种 资源限制 和 管理 的手段,比如可以限制线程的个数,动态新增线程等。每个 ThreadPoolExecutor 也保留了一些基本的统计数据,比如 当前线程池完成的任务数目等。
另外,线程池提供了许多可调参数 和 可扩展接口,已满足不同情境的需要,用户可以使用更方便的 Executors 的工厂方法,比如 newCachedThreadPool (线程池线程个数最多可达 Integer.MAX_VALUE ,线程自动回收)、newFixedThreadPool (固定大小的线程池) 和 newSingleThreadExecutor(单个线程)等来创建线程池。
文章目录
一、类图
可以看到,Executors 其实是个工具类,里面提供了许多静态方法,这些方法根据用户选择 返回不同的线程实例。 ThreadPoolExecutor 继承了 AbstractExecutorService ,成员变量 ctl 是一个 Integer 的原子变量,用来记录线程池状态 和 线程池中线程个数,类似于 ReentrantReadWriteLock 使用一个变量来保存两种信息。mainLock 是 独占锁,用来控制新增 Worker 线程操作的原子性。termination 是该锁对应的条件队列,在线程调用 await(Termination)
时,用来存放阻塞的线程。
Worker 继承 AQS 和 Runnable 接口,是具体承载任务的对象 。它继承了 AQS,自己实现了简单不可重入独占锁,其中 state = 0 表示 锁未被获取状态;state = 1 表示 锁已经被获取状态,state = -1 是创建 Worker 时默认的状态,创建时状态为 -1 是为了避免该线程运行 runWorker() 方法前被打断。Worker 中的变量 firstTask 记录该工作线程 执行的第一个任务,thread 是具体执行任务的线程。
DefaultThreadFactory 是线程工厂,newThread 方法是对线程的一个修饰。其中 poolNumber 是个静态的原子变量,用来统计线程工厂的个数,threadNumber 用来记录每个线程工厂 创建了多少线程,这两个值也作为 线程池 和 线程 的名称的一部分。
假设 Integer 类型是 32 位二进制表示,则 其中 高 3 位表示线程池状态,(后面 29 位用来记录线程个数), 默认是 RUNNING 状态,线程个数是0:
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
- 线程个数掩码位数
并不是所有平台的 int 类型都是 32 位,所以准确来说,具体平台下 Integer 的 二进制位数 -3 后的剩余位数 所表示的数 才是线程个数:
private static final int COUNT_BITS = Integer.SIZE - 3;
- 线程最大个数(低 29 位)
0001 1111 1111 1111 1111 1111 1111 1111 :
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
- 获取高 3 位 运行状态
private static int runStateOf(int c) { return c & ~CAPACITY; }
- 获取低 29 位 线程个数
private static int workerCountOf(int c) { return c & CAPACITY; }
- 计算 ctl 新值
private static int ctlOf(int rs, int wc) { return rs | wc; }
- 线程池状态
-
- RUNNING:接受新任务 并且 处理阻塞队列里的任务。
1110 0000 0000 0000 0000 0000 0000 0000
- RUNNING:接受新任务 并且 处理阻塞队列里的任务。
private static final int RUNNING = -1 << COUNT_BITS;
-
- SHUTDOWN:拒绝新任务 但是 处理阻塞队列里的任务。
0000 0000 0000 0000 0000 0000 0000 0000
- SHUTDOWN:拒绝新任务 但是 处理阻塞队列里的任务。
private static final int SHUTDOWN = 0 << COUNT_BITS;
-
- STOP:拒绝新任务 并且 抛弃阻塞队列里的任务,同时会中断正在处理的任务。
0010 0000 0000 0000 0000 0000 0000 0000
- STOP:拒绝新任务 并且 抛弃阻塞队列里的任务,同时会中断正在处理的任务。
private static final int STOP = 1 << COUNT_BITS;
-
- TIDYING:所有任务都执行完 (包含阻塞队列里的任务)后,当前线程池活动线程数为 0,将要调用 terminated 方法。
0100 0000 0000 0000 0000 0000 0000 0000
- TIDYING:所有任务都执行完 (包含阻塞队列里的任务)后,当前线程池活动线程数为 0,将要调用 terminated 方法。
private static final int TIDYING = 2 << COUNT_BITS;
-
- TERMINATED:终止状态。termianted 方法调用完成以后的状态。
0110 0000 0000 0000 0000 0000 0000 0000
- TERMINATED:终止状态。termianted 方法调用完成以后的状态。
private static final int TERMINATED = 3 << COUNT_BITS;
- 线程池状态转换:
- 线程池参数
-
- corePoolSize:线程池核心线程个数。
-
- workQueue:用于保存等待执行的任务的阻塞队列,比如 基于数组的有界 ArrayBlockingQueue、基于链表的无界 LinkedBlockingQueue、最多只有一个元素的同步队列 SynchronizedQueue 以及 优先级队列 PriorityBlockingQueu。
-
- maximunPoolSize:线程池最大线程数量。
-
- ThreadFactory:创建线程的工厂。
-
- RejectedExecutionHandler:饱和策略 / 拒绝策略。当队列满 并且 线程个数达到 maximunPoolSize 后采取的策略,比如 AbortPolicy 抛出异常、CallerRunsPolicy 使用调用者所在的线程来执行任务、DiscardOldestPolicy 调用 poll 丢弃一个任务,执行当前任务、 以及 DiscardPolicy 默默丢弃,不抛出异常。
-
- keepAliveTime:存活时间。如果当前线程池里的线程数量 比 核心线程数 多,并且是闲置状态,则 这些闲置的线程 能存活的最大时间。
-
- TimeUnit:存活时间的单位。
- TimeUnit:存活时间的单位。
- 线程池类型
-
- newFixedThreadPool:创建一个核心线程个数 和 最大线程个数 都为 nThreads 的线程池,并且阻塞队列的长度为 Integer.MAX_VALUE。keepAliveTime = 0 ,说明 只要线程个数比核心线程个数多 且 当前空闲,则被回收。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
-
- newSingleThreadExecutor:创建一个核心线程个数 和 最大线程个数 都为 1 的线程池,并且 阻塞队列长度为 Integer.MAX_VALUE。 keepAliveTime = 0,说明 只要线程个数比核心线程个数多 且 当前空闲,则被回收。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
-
- newCachedThreadPool:创建一个 按需创建线程 的线程池,初始线程个数为 0 ,最多线程个数为 Integer.MAX_VALUE,并且阻塞队列为同步队列。 keepAliveTime = 60,说明 只要线程个数比核心线程个数多 且 当前空闲,在 60 s 内被回收。这个类型的特殊之处在于,加入同步队列的任务会被马上执行,同步队列里最多只有一个任务。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
二、源码分析
(1) public void execute(Runnable command)
execute 方法的作用 是提交任务 command 到线程池进行执行。用户线程提交任务到线程池的模型图如下所示:
可以看到,ThreadPoolExecutor 的实现 实际上是一个 生产消费模型,当用户添加任务到线程池时 相当于 生产者 生成元素,workers 线程工作集 中的线程 直接执行任务 或 从任务队列中获取任务 则相当于消费元素。
源码:
public void execute(Runnable command) {
// 如果任务为 null,则抛出 NPE 异常
if (command == null)
throw new NullPointerException();
// 获取当前线程池的状态 和 线程个数
int c = ctl.get();
// 如果当前线程池中 线程个数 小于 核心线程池size
if (workerCountOf(c) < corePoolSize) {
// (一)就开启新线程运行,也就是 向 worker 中新增一个核心线程执行任务
if (addWorker(command, true))
return;
c = ctl.get();
}
// 如果如果当前线程池中 线程个数 大于等于 核心线程池size
// 如果线程池处于 RUNNING 状态,则 添加任务到阻塞队列
if (isRunning(c) && workQueue.offer(command)) {
// 二次检查
int recheck = ctl.get();
// 如果当前线程状态不是 RUNNING 则从队列中删除任务
if (! isRunning(recheck) && remove(command))
// 并执行拒绝策略
reject(command);
// 如果当前线程池为空,则添加一个线程
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 如果队列满,则新增线程
else if (!addWorker(command, false))
// 新增线程失败,说明任务队列已满,则执行拒绝策略
reject(command);
}
(一)addWorker :
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 检查队列是否只在必要时为空
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
// 循环 CAS 增加线程个数
for (;;) {
int wc = workerCountOf(c);
// 如果线程个数超限则返回 false
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 通过 CAS 增加线程个数,同时只有一个线程成功
if (compareAndIncrementWorkerCount(c))
// CAS 成功则退出双循环
break retry;
// CAS 失败则重新获取线程状态
c = ctl.get(); // Re-read ctl
// 如果线程状态变化了,则再次进入外层循环 获取状态
if (runStateOf(c) != rs)
continue retry;
// 否则继续 CAS 尝试
// else CAS failed due to workerCount change; retry inner loop
}
}
// 代码走到这里说明 CAS 成功了
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 创建 worker
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
// 获取独占锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 重新检查线程池状态,以避免获取锁前调用了 shutdown
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);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
// 如果添加成功 则 启动任务
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
addWorker 方法比较长,分为两部分,第一部分 双重循环,目的是通过 CAS 操作增加线程数 ; 第二部分 主要是把并发安全的任务 添加到 workers 中,并且启动任务执行。
注意到刚开始有一段:
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
展开 ! 运算后等价于:
rs >= SHUTDOWN &&
rs != SHUTDOWN ||
firstTask != null ||
workQueue.isEmpty()))
也就是说,在 addWorker 方法中,以下情况会返回 false:
- 当前线程池状态为 STOP、TIDYING 或 TERMINATED。
- 当前线程池状态为 SHUTDOWN 并且 已经有了第一个任务。
- 当前线程池状态为 SHUTDOWN 并且任务队列为空。
(2)工作线程 Worker 的执行
用户线程提交任务到线程池后,由 Worker 执行。
Worker 的构造方法:
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
可以看到,在构造方法里 首先设置 Worker 的状态为 -1,这是避免当前 Worker 在调用 newWorker 方法前被中断(当其他线程调用了线程池的 shutdownNow 时,如果 Worker 状态 >=0 则会中断该线程)。
run() 方法的源码:
public void run() {
// (一)
runWorker(this);
}
(一):
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) {
// 执行具体任务期间加锁,避免在任务运行期间,其他线程调用了 shutdown 后正在执行的任务被中断(showdown 只会中断当前被阻塞挂起的线程)
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;
// 统计当前 Worker 完成了多少个任务
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
// (二)执行清理工作
processWorkerExit(w, completedAbruptly);
}
}
(二) 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;
// 并从工作集中删除当前 worker
workers.remove(w);
} finally {
mainLock.unlock();
}
// 如果当前是 SHUTDOWN 状态 并且 队列为空,
// 或者 当前是 STOP 状态 并且 当前线程池中没有活动线程 ,
// 尝试设置线程池状态为 TERMINATED,
// 如果设置成了 TERMINATED 状态,还会调用条件变量 termination.signAll() 方法
// 激活所有因为调用线程池的 awaitTermination 方法而被阻塞的线程。
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);
}
}
(3) shutdown 操作
调用 shutdown 操作后,线程池就不会再接受新的任务了,但是 工作队列里的任务还是要执行的。该方法会立刻返回,并不等待队列任务完成后再返回。
源码:
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 权限检查
checkShutdownAccess();
// (一)设置当前线程状态为 SHUTDOWN,如果已经是 SHUTDOWN 则直接返回
advanceRunState(SHUTDOWN);
// (二)设置所有空闲线程的中断标志
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
// (四)尝试将状态变为 TERMINATED
tryTerminate();
}
可以看到,shutdown 方法中,是先调用了checkShutdownAccess() 方法进行权限检查,在这个方法中, 看 是否设置了安全管理器,是 则看 调用 shutdowwn 命令的线程 是否有关闭线程的权限,如果有,还有看调用线程是否有中断工作线程的权限,如果没有权限,则抛出 SecurityException 或者 NullPointerException 异常。
(一) advanceRunState:
private void advanceRunState(int targetState) {
for (;;) {
int c = ctl.get();
if (runStateAtLeast(c, targetState) ||
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
break;
}
}
可以看到,传入的参数是 SHUTDOWN,那么 如果当前线程池状态 >= SHUTDOWN 就会直接返回,否则设置为 SHUTDOWN 状态。
(二)interruptIdleWorkers():
private void interruptIdleWorkers() {
// (三)
interruptIdleWorkers(false);
}
(三):
private void interruptIdleWorkers(boolean onlyOne) {
// 加锁,同时只有一个线程可以调用 shutdown 方法设置中断标志
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
// 如果工作线程没有被中断,并且 没有正在运行 则设置中断标志
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
(四)tryTerminate():
final void tryTerminate() {
for (;;) {
int c = ctl.get();
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
terminated();
} finally {
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
可以看到,tryTerminate() 方法是先使用 CAS 设置当前线程池状态为 TIDYING,最后会调用 termination.signalAll(); 激活因为调用条件变量 termination 的 awaita() 系列方法而被阻塞的所有线程。
(4) shutdownNow 操作
调用 shutdownNow 操作后,线程池就不会再接受新的任务了,并且会丢弃工作队列里的任务,正在执行的任务会被中断,该方法会立刻返回,并不等待激活的任务执行完成,返回值是这时队列里被丢弃的任务列表。
源码:
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 权限检查
checkShutdownAccess();
// 设置线程池状态为 STOP
advanceRunState(STOP);
// (一)中断所有线程
interruptWorkers();
// 将队列任务移动到 tasks 中
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
(一)interruptWorkers 中断所有线程,包括 空闲线程 和 正在执行任务的线程:
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
(5) awaitTermination 操作
当线程调用 awaitTermination 方法后,当前线程会被阻塞,直到线程池状态变为 TERMINATED 才返回,或者等待时间超时才返回。
源码:
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
// 获取独占锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (;;) {
if (runStateAtLeast(ctl.get(), TERMINATED))
return true;
if (nanos <= 0)
return false;
nanos = termination.awaitNanos(nanos);
}
} finally {
mainLock.unlock();
}
}
可以看到,awaitTermination 方法中,先获取独占锁,然后在循环内部,会判断当前线程池状态是否至少是 TERMINATED 状态,如果是 则直接返回 true,否则 说明当前线程池中还有线程在执行,则 看设置的时间 nanos 是否小于 0,小于 0 则说明不需要等待,直接返回 false,如果大于 0 ,调用 termination.awaitNanos(nanos)
,期望在这段时间内 线程池状态会变为 TERMINATED。 当等待时间超时后,termination.awaitNanos(nanos); 就会返回,这时会继续循环,重新检查当前线程池状态是否为 TERMINATED ,如果是 直接返回,否则继续阻塞,挂起自己。
三、总结
线程池巧妙地使用一个 Integer 类型的原子变量 来记录 线程池状态 和 线程池中的线程个数。通过 线程池状态 来控制任务的执行,每个 worker 线程可以处理多个任务,线程池通过线程复用 减少了 线程创建 和 销毁的开销。