线程池参数
池状态和线程数量
// 使用一个Int类型32位同时表示:线程池状态、线程数量;高3位表示线程池状态,低29位表示线程数量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 29位表示线程数量位数
private static final int COUNT_BITS = Integer.SIZE - 3;
// 线程池的容量最大CAPACITY= 2^29 -1 约 500万
// 此处注意:-1 之后的二进制补码表示:001 00000000000000000000000000000 ===》 000 11111111111111111111111111111
// 高3位全是0 低29位全是1,为后续位运算做准备
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
/**
* 高3位表示线程池状态,从低到高依次是:
* RUNNING SHUTDOWN STOP TIDYING TERMINATED
* 111 000 001 010 011
*
* RUNNING:运行中,能接收新的任务(用户能提交新任务),对于已经接收的任务,会继续处理,这个是线程池正常的情况
* SHUTDOWN:关闭中,不再接收新的任务,对于已经接收的任务会继续处理完毕
* STOP:停止中,不再接收新的任务,对积压在阻塞队列的任务也不再处理,对于正在运行的任务的线程,会进行中断。
* TIDYING:整理中,不再接收新的任务,所有的任务都会被终止,释放线程池中的线程池,直到线程数量为0
* TERMINATED:终止状态,不接受、不运行、线程池被关闭了。
*
*/
// runState is stored in the high-order bits
// 线程池的状态
// 原码 10000000 00000000 00000000 00000001 --> 反码 11111111 11111111 11111111 11111110
// 补码 11111111 11111111 11111111 11111111 --> 左移 11100000 00000000 00000000 00000000
// 运行中 RUNNING = 111 00000 00000000 00000000 00000000
private static final int RUNNING = -1 << COUNT_BITS;
// 关闭 SHUTDOWN = 000 00000 00000000 00000000 00000000
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 停止 STOP = 001 00000 00000000 00000000 00000000
private static final int STOP = 1 << COUNT_BITS;
// 整理中 TIDYING = 010 00000 00000000 00000000 00000000
private static final int TIDYING = 2 << COUNT_BITS;
// 退出 TERMINATED = 011 00000 00000000 00000000 00000000
private static final int TERMINATED = 3 << COUNT_BITS;
因为是通过1个控制变量来同时控制状态和数量的,访问位的方法如下:
// Packing and unpacking ctl
private static int runStateOf(int c) {
// CAPACITY = 000 11111111111111111111111111111
// ~CAPACITY = 111 00000000000000000000000000000
// ctl & ~CAPACITY 就是取高3位,对应线程池状态
return c & ~CAPACITY;
}
// 取低29位,对应线程数量
private static int workerCountOf(int c) { return c & CAPACITY; }
// 更新ctl
private static int ctlOf(int rs, int wc) {
// rs 是线程池状态,高3位
// wd 是线程数量,低29位
// 按位或就是ctl的值
return rs | wc;
}
线程池的状态说明
- RUNNING:表示运行中,处于此状态的线程池,能接收新的任务(用户能提交新任务),对于已经接收的任务,会继续处理,这个是线程池正常的情况。
- SHUTDOWN:不再接收新的任务,对于已经接收的任务会继续处理完毕
- STOP:不再接收新的任务,对积压在阻塞队列的任务也不再处理,对于正在运行的任务的线程,会进行中断。
- TIDYING:不再接收新的任务,所有的任务都会被终止,释放线程池中的线程池,直到线程数量为0
- TERMINATED:终止状态,不接受、不运行、线程池被关闭了。
其它属性
// 核心线程数
private volatile int corePoolSize;
// 最大线程池数
private volatile int maximumPoolSize;
// 创建线程的工厂
private volatile ThreadFactory threadFactory;
// 阻塞队列,用户存放任务的容器
private final BlockingQueue<Runnable> workQueue;
// 线程容器,Worker就是工作者,一个Worker内部封装了一个Thread线程
// 这里使用HashSet进行存储,表示工作者容器,线程容器
private final HashSet<Worker> workers = new HashSet<Worker>();
// 互斥锁,由于上面的HashSet不是并发安全的,所以操作的时候肯定需要上锁
private final ReentrantLock mainLock = new ReentrantLock();
// 等待条件
private final Condition termination = mainLock.newCondition();
// 当前线程数 > corePoolSize时候,这部分多出的线程在空闲多久之后会被销毁掉
private volatile long keepAliveTime;
// 是否允许核心线程超时
// 如果是false,核心线程不会被销毁
// 如果是true,核心线程在keepAliveTime时间后依然空闲,则会被销毁掉
private volatile boolean allowCoreThreadTimeOut;
// 一个标记变量,标记曾经线程池的最大大小
// 举例:corePoolSize = 5, maximumPoolSize = 10
// 线程池最大的时候有过8个线程,那么largestPoolSize = 8,只是标记一些曾经的最大值
private int largestPoolSize;
// 一个统计变量,线程池已经完成的任务数量
private long completedTaskCount;
// 线程池拒绝策略
// 当线程池饱和(阻塞队列满了,存不进新的任务;同时线程池的数量达到了maximumPoolSize无法再增加新线程)的时候提交新任务会触发此策略的执行
// 当线程池状态为SHUTDOWN,表示我要关闭了,不再接收新任务;此时向线程池提交新任务也会触发此策略的执行
private volatile RejectedExecutionHandler handler;
// 默认的拒绝策略,直接抛出异常(Abort是中止的意思)
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();
核心源码分析
execute()执行任务
- 首先计算当前线程池的线程数,如果 当前线程数 < corePoolSize (核心线程数),则会新创建线程,然后将任务交给这个新线程执行,创建成功直接返回,创建新线程失败则继续下一步
- 当前线程 >= corePoolSize 的时候,判断当前线程池状态是否是RUNNING,如果是则优先会尝试将任务方进入阻塞队列。
- 当前线程数 >= corePoolSize,并且当前任务放入阻塞队列失败的时候,则会再次执行addWorker方法,尝试再去创建线程。
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*1. 如果运行的线程少于corePoolSize,则尝试用给定的命令作为第一个任务启动一个新线程。
* 对addWorker的调用会自动检查runState和workerCount,从而通过返回false来防止在不应该添加线程的情况下添加线程的错误警报。
*
* 2. 如果一个任务可以成功排队,那么我们仍然需要再次检查是否应该添加一个线程(因为自从上次检查以来已有的线程已经死亡),
* 或者在进入这个方法后池已经关闭。因此,我们重新检查状态,如果有必要,回滚排队停止,或启动一个新的线程,如果没有。
*
* 3.如果不能将任务排队,则尝试添加一个新线程。如果失败了,我们就知道自己被关闭或饱和了,所以拒绝了任务。
*
*/
// 获取线程池控制变量
int c = ctl.get();
// 1、当前线程数小于核心线程数
if (workerCountOf(c) < corePoolSize) {
// 1.1 创建一个核心线程,并将这个任务给这个线程去处理
if (addWorker(command, true))
// 1.2 说明采集线程成功并任务提交成功,true
return;
// 此处说明创建线程失败
c = ctl.get();
}
// 2、线程池运行中且任务可以提交到任务队列
if (isRunning(c) && workQueue.offer(command)) {
// 2.1 二次检查,因为上次检查后线程可能消亡了,或是池关闭了
int recheck = ctl.get();
// 说明池关闭了,那么移除掉新增的任务
if (! isRunning(recheck) && remove(command))
// 2.2 移除后调用拒绝策略
reject(command);
// 此处说明二次检查线程池RUNNING,但是没有线程
else if (workerCountOf(recheck) == 0)
// 所以创建非核心线程,因为任务已经提交了,新线程会从队列中获取任务去执行
addWorker(null, false);
}
// 3、此处说明阻塞队列添加任务失败了,说明队列满了
// 队列满如果最大线程数未满,创建线程来执行任务
else if (!addWorker(command, false))
// 创建线程失败,队列满,最大线程数饱和,只能走拒绝策略
reject(command);
}
addWorker()方法
// firstTask 第一个任务,非空则创建完成后执行这个任务
// core 是否根据核心线程限制,否则根据最大线程数限制
private boolean addWorker(Runnable firstTask, boolean core) {
// 标志下面是无限循环
retry:
// 循环重试
for (;;) {
// 控制变量
int c = ctl.get();
// 线程池状态
int rs = runStateOf(c);
// Check if queue empty only if necessary.
// 这里返回false,不允许创建新线程的情况有
// 1、状态非Running,也就是池关闭,且队列非空
// 2、状态非Running,并且task非空
// 3、状态是 STOP TIDYING TERMINATED
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 尝试加1
if (compareAndIncrementWorkerCount(c))
// cas 成功跳出循环
break retry;
c = ctl.get(); // Re-read ctl
// 走到这里说明 cas 失败,重试
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
// 走到这里说明数量加1成功了
// 标识Wie
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 创建新的工作者,内部包含一个线程,且task作为第一个任务
w = new Worker(firstTask);
// worker里面的线程
final Thread t = w.thread;
// 如果工作线程非空,下面的操作可能是将Worker放入HashSet容器了,加锁,释放锁、线程池状态校验之类的
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());
// 1、线程池状态 Running 可以继续
// 2、状态为关闭中国,但是此次没有新任务,也可以继续
// 往下创建新线程
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
// 将新创建的worker
workers.add(w);
int s = workers.size();
// 必要时候线程统计量
if (s > largestPoolSize)
largestPoolSize = s;
// 标识添加成功
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
// 启动work里面的内部线程
t.start();
workerStarted = true;
}
}
} finally {
// 如果启动失败了,执行失败操作,肯定是将worker从HashSet容器移除,线程数量减少一个
if (! workerStarted)
addWorkerFailed(w);
}
// 返回是否创建成功、启动成功标识
return workerStarted;
}
内部Worker类
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
// Worker内部持有的工作线程,就是依靠这个线程来不断运行线程池中的一个个task的
final Thread thread;
// 提交给这个Worker的首个任务
Runnable firstTask;
// 一个统计变量,记录着这个worker总共完成了多少个任务
volatile long completedTasks;
// 构造方法,创建Worker的时候给这个worker传入第一个运行的任务
Worker(Runnable firstTask) {
// 初始化AQS内部的state资源变量为-1
setState(-1);
// 保存一下首个任务
this.firstTask = firstTask;
// 使用线程工厂创建出来一个线程,这个线程负责运行任务
this.thread = getThreadFactory().newThread(this);
}
// 内部的run方法,这个方法执行一个个任务
public void run() {
// runWorker方法,去运行一个个task
runWorker(this);
}
// 实现AQS的互斥锁,这里是否持有互斥锁,不等于0就是持有
protected boolean isHeldExclusively() {
return getState() != 0;
}
// 实现AQS加互斥锁逻辑,就是CAS将state从0设置为1,成功就获取锁
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// 是新AQS释放互斥锁逻辑,就是将state变量从1设置为0,成功就释放锁成功
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
// 加锁
public void lock() { acquire(1); }
// 尝试加锁
public boolean tryLock() { return tryAcquire(1); }
// 解锁
public void unlock() { release(1); }
// 是否持有锁
public boolean isLocked() { return isHeldExclusively(); }
}
runWorker方法
- 在while循环里面,不断获取任务和执行任务
- 每次task.run执行之前都加锁,然后执行,然后解锁,防止被中断
- 如果循环中获取到的task为null,那么退出循环并销毁worker
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
// 获取第一个任务,赋值给task
Runnable task = w.firstTask;
// 置空方便gc
w.firstTask = null;
// 初始化同步器状态为0
w.unlock(); // allow interrupts
// 是否是突然被中断结束的标识
boolean completedAbruptly = true;
try {
// 重点!首个任务直接运行,否则不断地从队列中获取任务去执行
while (task != null || (task = getTask()) != null) {
// 执行任务内部都先加锁,防止执行过程中被中断
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
// 校验一下线程池的状态
// 如果是STOP TDIYING TERMINATED 那么需要中断当前线程
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
// 到了此处说明可以执行任务
try {
// 钩子函数
beforeExecute(wt, task);
// 抛出的异常
Throwable thrown = null;
try {
// 调用 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;
// 统计数量加1
w.completedTasks++;
// 解锁
w.unlock();
}
}
// 走到这里 说明退出了死循环,当前worker需要被销毁
completedAbruptly = false;
} finally {
// 销毁worker
processWorkerExit(w, completedAbruptly);
}
}
getTask()方法分析
根据当前的配置设置,对任务执行阻塞或定时等待,如果这个worker因为以下任何原因必须退出,则返回null:
- 有多于maximumPoolSize工作者(由于调用了setMaximumPoolSize)。
- 池已停止。
- 池被关闭,队列为空。
- 该工作线程在等待任务时超时,超时的工作线程在计时等待前后都会被终止(即allowCoreThreadTimeOut || workerCount > corePoolSize),如果队列非空,则该工作线程不是池中的最后一个线程
*/
private Runnable getTask() {
// 从阻塞队列中获取任务是否超时的标识
boolean timedOut = false; // Did the last poll() time out?
// 死循环
for (;;) {
int c = ctl.get();
// 获取线程池状态
int rs = runStateOf(c);
// Check if queue empty only if necessary.
// 如果状态是 STOP TIDYING TERMINATED 说明线程池关闭了直接返回null
// 如果状态是 SHUTDOWN ,同时阻塞队列为空,说明即将关闭,返回null
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
// 减少worker数量
decrementWorkerCount();
return null;
}
// 此处说明状态允许,计算当前线程数量
int wc = workerCountOf(c);
// Are workers subject to culling?
// 很重要!表示从阻塞队列中获取任务是否超时, allowCoreThreadTimeOut 控制核心线程是否超时
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// wc > maximumPoolSize 正常不会发生
// 此处说明获取任务超时了
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
// 减少worker数量 返回 null
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;
}
}
}
空闲线程被销毁processWorkerExit()
private void processWorkerExit(Worker w, boolean completedAbruptly) {
// completedAbruptly 表示当前线程是因为中断而被销毁的,需要扣减次数
// 正常超时被销毁为false
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();
}
// 尝试终止线程
tryTerminate();
int c = ctl.get();
// 如果当前线程池状态是 RUNNING 或是 STOP
if (runStateLessThan(c, STOP)) {
// 1.常规的超时关闭线程
if (!completedAbruptly) {
// 这就是计算一下当前线程池允许的最小线程数
// 正常情况是min=corePoolSize,但是当allowCoreThreadTimeout为true时候,允许销毁所有线程,则min=0
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
// 如果队列非,说明至少要保留1个线程去执行,不能销毁所有,会调用下面的 addWorker(null, false); 加一个
min = 1;
if (workerCountOf(c) >= min)
// 线程数量大于1,直接 返回即可
return; // replacement not needed
}
// 2.说明completedAbruptly == true,说明可能是因为线程被中断(interrupted方法)而被销毁
// 或是队列中还设有任务,需要补充一个新的worker
addWorker(null, false);
}
}
shutdown()优雅关闭线程池
核心实现在于销毁线程时,需要先获得worker内部的互斥锁,而在执行任务的过程中需要加锁,这样销毁线程操作时候不会直接中断线程,做到了优雅。
// 优雅关闭线程池
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
// 加锁
mainLock.lock();
try {
// 做一下安全检查
checkShutdownAccess();
// 改状态为 SHUTDOWN
advanceRunState(SHUTDOWN);
// 销毁线程池中的空闲线程
interruptIdleWorkers();
// 回调钩子
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
// 尝试终止线程池
tryTerminate();
}
// 销毁空闲的worker
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
// 加锁
mainLock.lock();
try {
// 遍历任务
for (Worker w : workers) {
Thread t = w.thread;
// 核心是要尝试获取锁,任务执行过程是加了锁的,获取不到锁就无法中断任务
// 优雅关闭的实现是要先获得锁
if (!t.isInterrupted() && w.tryLock()) {
try {
// 只有获取到互斥锁,才能中断线程,此处防止了任务执行过程中worker被中断
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
尝试终止线程池
final void tryTerminate() {
// 循环重试
for (; ; ) {
// 控制变量
int c = ctl.get();
// 这里直接return的情况,也就是不能进行中止线程池的情况有:
// 1. isRunning(c) 也就是当前线程池还是RUNNING正常运行,不能中止
// 2. 线程池为SHUTDOWN 并且 阻塞队列非空,说明还有一些任务没有执行,需要继续执行任务,不能中止
// 3. runStateAtLeast(c,TIDYING) 至少是TIDYING,也就是可能是TIDYING、TERMINATED这两种状态之一
// 处于TIDYING、TERMINATED 状态说明线程池已经关闭完成了,正在中止或者已经中止,这里就不需要再次中止了
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && !workQueue.isEmpty()))
return;
// 当前线程数不为0,有线程要先关闭线程,返回
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
}
// 走到这里,说明上面的检查情况都不满足
// 也就是: 状态是SHUTDOWN或是STOP,阻塞队列为空,线程数为0
final ReentrantLock mainLock = this.mainLock;
// 加锁
mainLock.lock();
try {
// 设置为 TIDYING 终止整理中
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
// 终止回调钩子
terminated();
} finally {
// 设置为TERMINATED 已终止
ctl.set(ctlOf(TERMINATED, 0));
// 唤醒因为终止条件而沉睡的线程
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
submit()方法
/**
* 提交任务返回 Future
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
// Callable 包装成 RunnableFuture
RunnableFuture<T> ftask = newTaskFor(task);
// 内部就是调用execute()
execute(ftask);
return ftask;
}
不提倡使用Executors来构建线程池
newCachedThreadPool方法
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0,
Integer.MAX_VALUE,
60L,
TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- corePoolSize为0,maximumPoolSize为MAX_VALUE、阻塞队列是SynchronousQueue。
- 每来一个任务就使用或是创建一个线程去处理,下子提交非常多的任务,会创建非常多的线程。
- 线程数量太多,cpu竞争剧烈,可能cpu 100%;同时由于每个线程数量需要占用一定内存,默认1M,所以可能导致内存资源耗尽而OOM
newFixdThreadPool 方法
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads,
nThreads,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- 问题是使用了
LinkedBlockingQueue
无界队列,阻塞队列最大容量是Integer.MAX_VALUE,相当于容量没有限制 - 大量任务进入LinkedBlockingQueue队列,而其又在内存中,会造成OOM
newSingleThreadExecutor方法
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(
1,1,0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
- 问题同样是无界队列
newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
// 内部调用父类ThreadPoolExecutor线程的构造方法
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
- DelayedWorkQueue延迟队列,无界,任务堆积会OOM