开发中,我们都有形或无形中用到线程池 (SpringBoot的@Async、Tomcat线程池 .....)。
-
线程池可以降低资源消耗(如果配置适当)。
-
线程池不用自己频繁创建、销毁线程。
-
能更好的管理线程,提高对线程的管理,并可查看执行任务数。
-
使用者设置参数,直接提交任务就行了
本文将涉及到的类会讲一讲,会有些长,请耐心看。
分析ThreadPoolExecutor之前我们先看看它的父接口和父类:
前奏
Executor
这个接口只有一个方法, 提交一个任务,没有返回任务的结果值。
void execute(Runnable command);Executor executor = anExecutor; executor.execute(new RunnableTask1()); executor.execute(new RunnableTask2());ExecutorService
这个继承了Executor,有更多契约。
// 任务继续执行并执行队列剩余的任务。不接受新任务。 void shutdown(); // 一旦调用,停止工作线程的任务(工作线程需响应中断,否则无法停止),不执行队列剩余任务,返回队列的任务。 List<Runnable> shutdownNow(); // 返回线程池是否Shutdown状态 boolean isShutdown(); // 返回线程池是否完全关闭状态 boolean isTerminated(); // 一直阻塞,直到线程池状态Terminated后回调。 // true 线程池成功关闭后返回,false 超时 boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException; //响应中断 // 有返回值的任务提交。 函数式接口Callable<V>接口方法: V call() throws Exception; // 如果想要阻塞等待任务返回结果:exec.submit(aCallable).get(); <T> Future<T> submit(Callable<T> task); // T result 自定义返回结果的类型 <T> Future<T> submit(Runnable task, T result); Future<?> submit(Runnable task); // 执行给定任务,当他们全部执行完成,返回结果和状态。 Future#isDone 任务可能正常完成终止或通过抛出异常终止 <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException; // timeout 等待的最长时间, unit 超时参数的时间单位, //如果任务执行超时,这任务的状态将不是完成。 <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException; // 任务集合,只要有一个完成即可。未完成的任务将被取消 ExecutionException这个异常是没有任务成功完成 <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException; // 同样的,只是设置了超时。TimeoutException这个异常是: 指定时间过去了,但是没有一个任务完成 <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;AbstractExecutorService
抽象类,实现ExecutorService接口。提供ExecutorService的默认实现。
// value 返回的默认值 protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { return new FutureTask<T>(runnable, value); } public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFuture<Void> ftask = newTaskFor(task, null); execute(ftask); return ftask; } // 下面的submit都差不多,只是任务类型不同。 ... // 注意这个方法,invokeAny都会用到 private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks, boolean timed, long nanos) throws InterruptedException, ExecutionException, TimeoutException { // 校验 if (tasks == null) throw new NullPointerException(); int ntasks = tasks.size(); if (ntasks == 0) throw new IllegalArgumentException(); // 存储任务返回值列表 ArrayList<Future<T>> futures = new ArrayList<Future<T>>(ntasks); // 这个是安排提交的任务。 // 这个类实现CompletionService接口。内部其实还是用的是AbstractExecutorService,可扩展。 // 里面有存储完成任务的队列(completionQueue),对于任务的获取(take、poll、poll(timeout, unit)) ExecutorCompletionService<T> ecs = new ExecutorCompletionService<T>(this); try { // 记录异常 ExecutionException ee = null; // 是否设置了超时 final long deadline = timed ? System.nanoTime() + nanos : 0L; // 任务迭代器 Iterator<? extends Callable<T>> it = tasks.iterator(); // 开始一个任务,并且存储返回值。 futures.add(ecs.submit(it.next())); // 待执行任务数-1 --ntasks; // 已添加任务数+1 int active = 1; for (;;) { // 注意这里返回的是FutureTask<T>(callable); Future<T> f = ecs.poll(); // 代表没有已完成的任务。 if (f == null) { if (ntasks > 0) { // 待执行任务数-1 --ntasks; // 重新加一个任务 futures.add(ecs.submit(it.next())); // 已添加任务数+1 ++active; } // 注意这里,一开始有人任务提交都是+1,那么什么时候为0呢? // 玄机就是在下面f!=null的时候,如果下面任务正常完成了那么肯定会return,除非异常, // 注意:下面 if (f != null)并没有 throw e,抛异常操作是在自旋之外。 // 也就是说,这里的active==0,是所有任务都失败了。必须出去。 else if (active == 0) break; // 检验是否超时 else if (timed) { // 获取任务执行结果 f = ecs.poll(nanos, TimeUnit.NANOSECONDS); if (f == null) throw new TimeoutException(); nanos = deadline - System.nanoTime(); } else //阻塞获取已完成的任务 f = ecs.take(); } // 说明已经有完成的任务了, if (f != null) { // 减提交的任务数。 --active; try { // 阻塞获取,除非有异常 return f.get(); } catch (ExecutionException eex) { ee = eex; } catch (RuntimeException rex) { ee = new ExecutionException(rex); } } } // 这里就是接自旋的异常。 if (ee == null) ee = new ExecutionException(); throw ee; // 无论是否正常完成,都取消其他任务。 } finally { // 取消其他任务 for (int i = 0, size = futures.size(); i < size; i++) futures.get(i).cancel(true); } } FutureTask
- 一个可以取消的异步计算,get()将阻塞如果没完成任务
- 可用于包装Callable或Runnable 对象
- FutureTask可以提交给Executor用于执行
- 状态转换:
- NEW -> COMPLETING -> NORMAL
- NEW -> COMPLETING -> EXCEPTIONAL
- NEW -> CANCELLED
- NEW -> INTERRUPTING -> INTERRUPTED
下面我们看一看FutureTask
private volatile int state; // 任务状态(state)。 private static final int NEW = 0; private static final int COMPLETING = 1; private static final int NORMAL = 2; private static final int EXCEPTIONAL = 3; private static final int CANCELLED = 4; private static final int INTERRUPTING = 5; private static final int INTERRUPTED = 6; // 任务返回值 或者 异常 // 注意没有volatile ,受读写保护 //只有cas成功的线程才能赋值。 private Object outcome; //执行callable的线程 private volatile Thread runner; // 等待的线程节点 private volatile WaitNode waiters; public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; } // 注意这个 result 是给定的返回值 public FutureTask(Runnable runnable, V result) { // 任务适配器 返回RunnableAdapter类型。如果调用callable.call(),任务将会执行,完成后返回result this.callable = Executors.callable(runnable, result); this.state = NEW; } // 这个只要是CANCELLED、INTERRUPTING、INTERRUPTED状态,就是认为任务取消了 public boolean isCancelled() { return state >= CANCELLED; } // 只要不是新建状态的任务都是Done状态,包括阻塞、异常、正常状态 public boolean isDone() { return state != NEW; } public V get() throws InterruptedException, ExecutionException { int s = state; //只要是NEW、COMPLETING状态,就阻塞获取。 if (s <= COMPLETING) //当多个线程进入时,等待任务完成。 s = awaitDone(false, 0L); // 这里只是任务状态是NORMAL才成功返回,否则抛异常外面接。 return report(s); } // 此方法响应中断。 private int awaitDone(boolean timed, long nanos) throws InterruptedException { //是否设置了超时。 final long deadline = timed ? System.nanoTime() + nanos : 0L; WaitNode q = null; boolean queued = false; // 自旋。 for (;;) { // 如果该线程被中断了,那么移除等待节点, if (Thread.interrupted()) { //移除当前节点,第一次进来,q是为空的,也就是什么也不做。 removeWaiter(q); throw new InterruptedException(); } int s = state; // 不是新建状态,也不是执行任务的状态,返回state。 if (s > COMPLETING) { if (q != null) q.thread = null; return s; } // 这个任务是在执行中,让出CPU调度 else if (s == COMPLETING) // cannot time out yet Thread.yield(); else if (q == null) //任务是新建状态,包装当前线程。 q = new WaitNode(); else if (!queued) //来到这里,就是线程包装WaitNode好了,进入链表等待。这个是cas入队新创建的当前线程。头插法。等待 queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q); // 检验是否超时等待 else if (timed) { nanos = deadline - System.nanoTime(); if (nanos <= 0L) { removeWaiter(q); return state; } LockSupport.parkNanos(this, nanos); } else //线程入队了,可以阻塞了。一旦任务执行完成,也就是status>COMPLETING, //finishCompletion();后会LockSupport.unpark(t); //线程又会从这里醒来,再继续自旋,满足条件后return state LockSupport.park(this); } } // s等于status private V report(int s) throws ExecutionException { Object x = outcome; //NORMAL完成了任务,就返回 if (s == NORMAL) // 返回outcome return (V)x; // 任务状态是CANCELLED、INTERRUPTING、INTERRUPTED,抛出取消异常。 if (s >= CANCELLED) throw new CancellationException(); //如果都不是,就是执行异常。 throw new ExecutionException((Throwable)x); } // 移除中断、超时的节点, private void removeWaiter(WaitNode node) { // 注意这个细节,设置了thread为空,下面自旋有用。 if (node != null) { node.thread = null; //标识外层循环 retry: for (;;) { //不为空就遍历单向链表 for (WaitNode pred = null, q = waiters, s; q != null; q = s) { //下一个节点。 s = q.next; //只要是不为空的thread,就不是刚开始设置的node。 //那么就一直找 if (q.thread != null) //记录前面的节点 pred = q; //如果找到了当前thread为空。 else if (pred != null) { //那么上一个节点,连接下一个节点。 pred.next = s; //检验是否被其他线程改。这部分没搞懂。 if (pred.thread == null) // check for race continue retry; } //如果pred为空,当前节点,q.thread为空 //交换当前节点为下一个节点。 else if (!UNSAFE.compareAndSwapObject(this, waitersOffset, q, s)) continue retry; } // 这里就是q=null,跳出自旋。 break; } } } // 设置返回值 protected void set(V v) { //前提是任务为新建状态。 if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = v; UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state finishCompletion(); } } // 设置返回异常结果 protected void setException(Throwable t) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = t; UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state finishCompletion(); } } public void run() { // 保证任务是新建状态。如果是新建状态,设置执行任务的线程 if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { //执行任务,并拿到返回值。 result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; // 设置异常原因 setException(ex); } //任务执行完成 if (ran) //设置任务执行状态,NEW->COMPLETING,设置任务的执行结果:outcome=result。 //成功后在设置任务状态(UNSAFE.putOrderedInt):COMPLETING->NORMAL set(result); } } finally { // 无论是否正常完成,清除当前任务的线程。防止并发调用run runner = null; int s = state; //响应中断。 if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } } // 此方法没设置result. protected boolean runAndReset() { if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return false; boolean ran = false; int s = state; try { Callable<V> c = callable; if (c != null && s == NEW) { try { c.call(); ran = true; } catch (Throwable ex) { setException(ex); } } } finally { runner = null; // 再获取一下状态,响应中断 s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } // 执行完成了并且还是任务还是新建状态 return ran && s == NEW; } // 取消任务方法。 // mayInterruptIfRunning状态标识:true :INTERRUPTING false :CANCELLED public boolean cancel(boolean mayInterruptIfRunning) { if (!(state == NEW && UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED))) // 任务状态不是新建的,或者cas失败 return false; try { if (mayInterruptIfRunning) { try { Thread t = runner; if (t != null) // 中断 t.interrupt(); } finally { // final state // 设置INTERRUPTED状态。 UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); } } } finally { finishCompletion(); } return true; } // run方法的finally都用这个,用于唤醒线程并且把等待节点清空。 private void finishCompletion() { for (WaitNode q; (q = waiters) != null;) { // cas设置 WaitNode为空 if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) { for (;;) { Thread t = q.thread; // 只要还有thread,就一直唤醒。并且清空WaitNode节点。 if (t != null) { q.thread = null; LockSupport.unpark(t); } WaitNode next = q.next; if (next == null) break; q.next = null; // unlink to help gc q = next; } break; } } // 子类实现 done(); // 已经执行完成了,无任务了。 callable = null; }中篇
ThreadPoolExecutor
说了这么多,前面是铺垫,现在讲线程池了(全是重点),仔细读一读,不禁感叹,doug lea大师就是大师,既能把所有情况都考虑到,又能把代码写的如此简洁。让我们体会一下细节的力量,细节是洪水猛兽。
把线程池化是提高资源的利用率,是统一管理资源,监控已有资源状态。
这里记录下个人对源码结合注释的解读。
线程池的生命周期状态:
- RUNNING
- SHUTDOWN
- STOP
- TIDYING
- TERMINATED
// workerCount : 有效的线程数量,存活线程。
// runState : 线程池状态。
// 限制线程数 : (2^29)-1 (about 500 million)
//------线程池状态
// RUNNING : 接受新任务,执行阻塞队列的任务
// SHUTDOWN : 不接受任务,但会执行阻塞队列的任务,以及继续执行正在执行的任务。
// STOP : 不接受任务,也不会执行阻塞队列的任务,同时中断正在执行的任务的线程。
// TIDYING : 所有任务完成,存活线程数为0,线程池状态转向tydying状态,并调用结束terminated(),钩子方法,
// TERMINATED: 当terminated()完成。
// 状态转换 : RUNNING -> SHUTDOWN call shutdown()
// SHUTDOWN -> TIDYING 阻塞队列无任务,线程无存活。
// STOP -> TIDYING 存活线程为空
// TIDYING -> TERMINATED 当terminated()完成
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
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;
// Packing and unpacking ctl
// 线程池的运行状态 (runState) 和线程池内有效线程的数量 (workerCount),
// 高3位保存runState,低29位保存workerCount,两个变量之间互不干扰。用一个变量去存储两个值,可避免在做相关决策时,出现不一致的
// 情况,不必为了维护两者的一致,而占用锁资源。
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
// 通过状态和线程数生成ctl
private static int ctlOf(int rs, int wc) { return rs | wc; }
// 阻塞队列
private final BlockingQueue<Runnable> workQueue;
// 存活线程
private final HashSet<Worker> workers = new HashSet<Worker>();
private final ReentrantLock mainLock = new ReentrantLock();
// 条件队列
private final Condition termination = mainLock.newCondition();
// 当前最大线程池数
private int largestPoolSize;
// 完成任务数
private long completedTaskCount;
// 创建线程的线程工厂
private volatile ThreadFactory threadFactory;
// 拒绝策略(当线程池最大线程数为峰值并且队列已满,执行拒绝策略)
private volatile RejectedExecutionHandler handler;
// 当超过核心线程数的线程还有设置允许allowCoreThreadTimeOut,则使用此超时时间,
private volatile long keepAliveTime;
// 当此变量为false(默认),核心线程在没工作的时候也存活,总是等待任务。
private volatile boolean allowCoreThreadTimeOut;
// 核心线程数
private volatile int corePoolSize;
// 最大线程数
private volatile int maximumPoolSize;
// 默认拒绝策略,抛异常
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();
图1 提交任务的判断
经上图的提交任务的策略顺序来看,阻塞队列更像一个起到缓冲作用的用法,当核心线程都在跑任务,还要提交任务,就进缓冲。注意这个缓冲,如果把设置的Integer最大,如果场景的任务非常多。是很容易OOM的。因为非核心线程没用上,更不用说拒绝策略了。
线程池的七大参数,根据场景设置,事实是这个并不好设置,需要经验。
构造方法:
// 七个参数:corePoolSize核心线程数、
// maximumPoolSize最大线程数、
// keepAliveTime默认允许非核心线程存活时间,unit时间单位,workQueue阻塞队列
// threadFactory创建线程的工厂。
// RejectedExecutionHandler 阻绝策略
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
提交任务的入口:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
// 这里成功后,返回出去,任务成功添加,并且是核心线程执行。
if (addWorker(command, true))
return;
// 到了这里,线程池状态发生了改变,再看看线程池状态,>SHUTDOWN,
c = ctl.get();
}
// 来到这线程池状态发生了改变,或者线程数大于等于核心线程数,任务会入队。
// 线程池状态是RUNNING,并且任务成功入队,
if (isRunning(c) && workQueue.offer(command)) {
// 再次查看线程池状态。
int recheck = ctl.get();
// 一旦>RUNNING,就不接受提交任务,执行拒绝策略。
if (! isRunning(recheck) && remove(command))
reject(command);
// 来到这,说明线程池状态正常:<=SHUTDOWN,这里判断下工作线程是不是为0,毕竟还有任务,
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 这里的情况是,当前线程数大于核心线程数,并且任务队列满了,尝试创建 thread,此时若成功,即:workers >=corePoolSize,
// workers< maximumPoolSize,否则不是正常的线程池状态,执行拒绝策略。
else if (!addWorker(command, false))
reject(command);
}
我们可以看到,提交任务跟addWorker关系密切,线程池的状态尤为重要。
好了,这儿我们先看看Worker
//我们看到 worker继承了AQS,用独占不可重入的设计。每个woker都有自己锁,防止执行任务时被中断。
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L;
// 记录线程
final Thread thread;
// 任务
Runnable firstTask;
// 每个线程的执行完成的任务数
volatile long completedTasks;
Worker(Runnable firstTask) {
//注意这里,非常重要,这个-1,防止中断,包括shutDownNow的方法中断。
setState(-1);
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
// 线程开始执行自己的任务时,先执行外界给的firstTask,完成后,自旋从队列取任务(阻塞)
public void run() {
runWorker(this);
}
// Lock methods
//
// The value 0 represents the unlocked state.
// The value 1 represents the locked state.
//是否自己持有锁。
protected boolean isHeldExclusively() {
return getState() != 0;
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
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(); }
// shutDownNow()使用,相当于worker自杀。
void interruptIfStarted() {
Thread t;
// getState大于0才能自我中断,也就是在执行构造方法的时候不能中断。
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
// 到这就是,每个线程执行的方法。
// 太妙了,全是重点。
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
// 拿到使用者给的第一个任务
Runnable task = w.firstTask;
w.firstTask = null;
// 这儿是因为在构造worker设置状态为-1, unlock后,也就是release(1)
// release会tryRelease,其中 setState(0);这是中断的前提条件
// 因为中断可以是tryLock,也可以自我中断,但首先需满足getStaus()!=0
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
// getTask()可能会阻塞住,也可能返回为空
while (task != null || (task = getTask()) != null) {
// 注意,这个是不可重入锁,到了这里,有任务,并且不会响应shutdown中断。
w.lock();
// 到了这里会检查下是否达到中断自己的条件
// 检验线程池状态,如果线程池是STOP、TIDYING、TERMINATED状态,确保自己中断
// 这个用意是响应shutDownNow的线程自杀。也就是不让执行任务了。
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();
// 抓异常抛出并给到thrown,用于finally的afterExecute
} 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=true),
// 如果到了这里,在getTask()返回了空,
completedAbruptly = false;
} finally {
// 如果来到这,就是线程执行任务时,有异常了或者getTask()返回了空。线程本该执行任务完成后从任务队列取任务。
processWorkerExit(w, completedAbruptly);
}
}
线程从阻塞队列获取任务:
// 从阻塞队列获取任务。
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.
// 老规矩,检查线程池状态。
// 线程池状态是SHUTDOWN、STOP、TIDYING、TERMINATED
// 并且,>= (STOP、TIDYING、TERMINATED)或者任务队列为空
// 我们可以看到,即使SHUTDOWN状态,任务队列还有任务,就不会返回
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
//减完工作线程数就返回了。
return null;
}
// 线程数
int wc = workerCountOf(c);
// Are workers subject to culling?
// 允许核心线程空闲超时,或者线程数超过了核心线程数。
// 这个超时设置,我们可以看到,如果是使用者可以定义allowCoreThreadTimeOut为true,或者有非核心线程了。
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// (wc > maximumPoolSize || (timed && timedOut)) :
// 只要是超过最大线程数。 或者允许了核心线程空闲超时、有非核心线程活动,但是没有任务
// timedOut 在下面判断
// 我们可以看到,只要线程数没超过最大线程数,没有允许核心线程空闲超时,并且wc<=核心线程,就不会返回空,会阻塞获取任务
// 返回空的,是当前线程数超过核心线程数,并且队列没任务,
// 那么线程会返回到processWorkerExit,进行清理空闲线程操作,
// completedAbruptly = false;为了干掉超过核心线程数的线程
if (
(wc > maximumPoolSize || (timed && timedOut))
&&
// 注意:是大于1,只要有2个线程以上存活、或者线程数为1却没任务
(wc > 1 || workQueue.isEmpty())) {
// 减去多余的线程
if (compareAndDecrementWorkerCount(c))
return null;
// 自旋,compareAndDecrementWorkerCount失败,发生竞争。
continue;
}
try {
// timed = allowCoreThreadTimeOut || wc > corePoolSize;
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
// 到了这里,说明不允许核心线程空闲超时,而且没有非核心线程,这个take是会阻塞的。也就是没有任务,一直在等。
workQueue.take();
if (r != null)
return r;
// 拿到任务为空。说明可以尝试 kill 线程了
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
在getTask中,会检查线程池状态,并且符合条件杀掉多余的线程。
下面我们看看添加工作线程的方法。太妙了。
private boolean addWorker(Runnable firstTask, boolean core) {
// 外层循环
// 注意这里没有上大锁。
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
// 检查线程池状态和队列的状态。
// 线程池状态>=(SHUTDOWN、STOP、TIDYING、TERMINATED)
if (rs >= SHUTDOWN &&
// 能来到这,说明线程池状态非正常,
// 这个条件( rs == SHUTDOWN, firstTask == null , ! workQueue.isEmpty() )
// 只要有一个为false,就符合退出条件。
// SHUTDOWN,要是还提交任务就是不允许啦。
// 我们看到即使SHUTDOWN状态, firstTask为空(这个是其他地方有用),
// 队列还有任务,就不会返回false,也就是说,会继续创建线程
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
// 里层循环
for (;;) {
// 获取工作线程数
int wc = workerCountOf(c);
// 结合核心最大线程数、最大线程数限制,判断是否符合创建线程条件,
if (wc >= CAPACITY ||
// 注意:
// 如果是core,大于等于 corePoolSize,不让创建线程
// 如果非核心线程,大于等于maximumPoolSize,不让创建线程
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
}
}
// 到了这里说明上面成功增加了线程数。
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
// 注意:到了这里,才拿这把大锁。只允许线程池中一个线程进入创建线程。
// 另外,tryTerminate(),也会拿这个把大锁,也就是说,一旦此线程拿到了这把大锁,
// 那么,尝试关闭线程池的线程让他等着,等我先造一个线程完后再关闭吧。
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
int rs = runStateOf(ctl.get());
// 再次检查线程池状态,如果为SHUTDOWN状态,firstTask为空,还创个啥
// 这里抛出了IllegalThreadStateException异常,但是没有catch,抛到外层
// 哪里会出现firstTask为空呢,
// 也就是在: public void setCorePoolSize(int corePoolSize)
// public boolean prestartCoreThread() 提前加一个核心线程。
// void ensurePrestart()
// public int prestartAllCoreThreads() 提前启动所有核心线程。相当于预热。
// private void processWorkerExit。处理线程退出。
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) {
//这里会执行runWorker方法,
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
// 这里就是有异常后处理 completedAbruptly可以看做异常、任务队列没有任务的标注
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;
// 移除当前的线程。此线程可能时执行任务有异常,或者线程池的线程数超过核心线程数并且队列没有任务,
workers.remove(w);
} finally {
mainLock.unlock();
}
// 尝试关闭线程,
tryTerminate();
int c = ctl.get();
// 如果线程池在 RUNNING SHUTDOWN状态
if (runStateLessThan(c, STOP)) {
// completedAbruptly=false ,getTask()为空。来到了这。
if (!completedAbruptly) {
// allowCoreThreadTimeOut:允许核心线程超时。 允许的话,等空闲超时后线程数就是为0了,
// 因为都允许核心线程空闲超时了,还有什么可留恋的。
// 如果不允许,那就最小的线程数的就是corePoolSize数。
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
// 有可能队列新提交了任务,线程不能死。
if (min == 0 && ! workQueue.isEmpty())
// 留个活口
min = 1;
// 先看看线程池的线程数是不是符合条件,符合了就不用增加线程了。
if (workerCountOf(c) >= min)
return; // replacement not needed
}
// 到了这里,就是队列还有任务,但是没有线程
addWorker(null, false);
}
}
// 关闭线程池:
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 提前设置线程池的状态,自旋cas
// 里面会先看线程池状态是否已经被其他线程设置了,这样就不用再重复设置了。
advanceRunState(SHUTDOWN);
// 这里会中断所有workers线程,
// 注意里面的interruptIdleWorkers是false(如果为true,只是tryLock一次),也会有把大锁(可重入),
// 但是仅仅是tryLock(),也就是在执行任务的线程不会中断。
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
// shutdown用的是false
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
// 第二个条件,不可重入锁,已经在执行任务的线程不会中断。tryLock是cas 0 -> 1
// 失败了就下一个worker
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
// 符合关闭条件,则会自旋一个一个中断线程
final void tryTerminate() {
// 自旋,
for (;;) {
int c = ctl.get();
// 线程池状态为 RUNNING,不让关闭
if (isRunning(c) ||
// 或者线程池状态为 TIDYING、TERMINATED,说明其他线程已经在关闭了,此时,不用尝试关闭了
runStateAtLeast(c, TIDYING) ||
// 来到这里就是SHUTDOWN、STOP状态
// 线程池状态为SHUTDOWN但是还有任务,就不关闭了
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
// 前面进行状态检验,
// 来到这就说明可以关闭了。
// 这个时候,线程池状态可能是SHUTDOWN但是队列没任务,也可能是STOP状态
if (workerCountOf(c) != 0) { // Eligible to terminate
// 看看尝试中断一个线程。
// 其实这个时候 工作线程会在getTask(),看线程池状态,是shutdown的话,并且任务队列为空
// 自己会先减工作线程数的。decrementWorkerCount,并且retuen null。这个时候就用到processWorkerExit。
interruptIdleWorkers(ONLY_ONE);
return;
}
// 到了这里,符合关闭线程池了,工作线程数为0
// 拿大锁,只允许一个线程。
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// cas线程池状态,准备关闭,先设置TIDYING状态,并且工作线程数为0
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
// 子类实现
terminated();
} finally {
// 设置成功后,在设置成TERMINATED状态
ctl.set(ctlOf(TERMINATED, 0));
// 这个时候 唤醒条件队列在等待TERMINATED的条件的线程,
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
马上关闭:
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 提前设置线程池状态。
advanceRunState(STOP);
// 注意,这个是线程自杀
interruptWorkers();
// 拿到任务队列的任务
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
// 这里跟shutdown不同的是,这里会执行worker自己的中断方法,不用尝试加锁,也就是正在执行任务的线程也会中断。
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
尾声
好了,到了这就是接近尾声了,我们看看一些辅助方法:
// 这个方法是等待指定时间,看能不能等到线程池关闭后,用户做一些生命周期的操作
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();
}
}
还可以设置核心线程数,最大线程数,keepAliveTime,拒绝策略,线程工厂。
public void setThreadFactory(ThreadFactory threadFactory)
public void setRejectedExecutionHandler(RejectedExecutionHandler handler)
public void setCorePoolSize(int corePoolSize) {
if (corePoolSize < 0)
throw new IllegalArgumentException();
int delta = corePoolSize - this.corePoolSize;
this.corePoolSize = corePoolSize;
if (workerCountOf(ctl.get()) > corePoolSize)
// 中断空闲的线程。
interruptIdleWorkers();
// 如果是调大了核心线程数
else if (delta > 0) {
// 取相差了的核心线程数,和任务队列的任务数做对比。取最小的数
int k = Math.min(delta, workQueue.size());
// 1:取任务队列的数,说明新增的核心线程比任务队列的数还要大。启动核心线程拿任务
// 2:取新增的核心线程的数,说明任务队列的数比新增的核心线程数还要大。
while (k-- > 0 && addWorker(null, true)) {
// 只要任务队列为空就不加线程了。
if (workQueue.isEmpty())
break;
}
}
}
public void setMaximumPoolSize(int maximumPoolSize)
public void setKeepAliveTime(long time, TimeUnit unit)
// 清除被取消的任务
public void purge()
// 获取正在工作的线程
public int getActiveCount()
// 获取线程数达到的峰值
public int getLargestPoolSize()
// 获取线程完成的任务(包括有异常的任务)+正在执行的任务+任务队列的任务
public long getTaskCount()
// 允许核心线程超时回收
public void allowCoreThreadTimeOut(boolean value)
拒绝策略(当然可以自己实现RejectedExecutionHandler自定义自己的拒绝策略),我们看看线程池默认提供的拒绝策略:
- CallerRunsPolicy : 拿去任务的线程自己执行(前提是线程池状态正常run)
- AbortPolicy: 默认抛RejectedExecutionHandler异常
- DiscardPolicy:忽略此任务
- DiscardOldestPolicy:把任务队列的最老的任务弹出,新增现在提交的任务
结束。