ThreadPoolExecutor是线程池的框架。虽然好多大佬都分析过了,为了加深理解,今天我也来分析一下ThreadPoolExecutor的源码
ThreadPoolExecutor这个类上面的英文注释已经很详细了,一看就能明白。这部分就直接把对应的英文翻译成中文。
下面这一段中文就全部是类上面的英文的翻译
一个 ExecutorService 使用可能的几个池线程之一执行每个提交的任务,通常使用 Executors 工厂方法配置。
线程池解决两个不同的问题:由于减少了每个任务的调用开销,它们通常在执行大量异步任务时提供改进的性能,并且它们提供了一种限制和管理资源的方法,包括在执行集合时消耗的线程任务。 每个ThreadPoolExecutor还维护一些基本的统计信息,例如已完成的任务数。
为了在广泛的上下文中有用,此类提供了许多可调整的参数和可扩展性hooks。 但是,强烈建议程序员使用更方便的 Executors工厂方法 Executors.newCachedThreadPool(无界线程池,具有自动线程回收)、Executors.newFixedThreadPool(固定大小线程池)和 Executors.newSingleThreadExecutor(单个后台线程),它们为最常见的使用场景进行了预先配置。
否则,在手动配置和调整此类时使用以下指南:
-
线程池中核心线程和最大线程大小
ThreadPoolExecutor 将根据 corePoolSize和 maximumPoolSize设置的线程池大小。
在方法 execute(Runnable) 中提交新任务时,
- 如果正在运行的线程少于 corePoolSize,即使其他工作线程处于空闲状态,也会创建一个新线程来处理请求,
- 如果正在运行的线程数大于corePoolSize同时少于 maximumPoolSize,则只有在队列已满时才会创建一个新线程来处理请求。
通过将 corePoolSize 和 maximumPoolSize 设置为相同,就可以创建一个固定大小的线程池。
通过将maximumPoolSize 设置为本质上无界的值,例如Integer.MAX_VALUE,可以允许池容纳任意数量的并发任务。
最常见的是,核心和最大池大小仅在构造时设置,但它们也可以使用 setCorePoolSize 和 setMaximumPoolSize 动态更改。
-
按需构建
默认情况下,核心线程只有在新任务到达时才最初创建和启动,但这可以使用方法 prestartCoreThread 或 prestartAllCoreThreads 动态覆盖。如果使用非空队列构造池,可能想要预启动线程。
-
创建新线程
使用 ThreadFactory方法创建新线程。如果没有另外指定,则使用 Executors.defaultThreadFactory,它创建的线程都在同一个 ThreadGroup 中,并且具有相同的 NORM_PRIORITY 优先级和非守护进程状态。通过提供不同的 ThreadFactory,可以更改线程的名称、线程组、优先级、守护程序状态等。执行任何任务。线程应该拥有“modifyThread”运行时权限。
如果工作线程或其他使用池的线程不具备此权限,则服务可能会降级:配置更改可能无法及时生效,关闭池可能会一直处于可以 终止但未完成的状态。
-
保活时间
如果线程池中当前有超过 corePoolSize 的线程,则如果空闲时间超过 keepAliveTime,多余的线程将被终止。这提供了一种在不繁忙使用线程池时减少资源消耗的方法。如果线程池稍后变得更加繁忙,则将构建新线程。也可以使 setKeepAliveTime(long, TimeUnit) 方法动态更改此参数。使用 Long.MAX_VALUE TimeUnit.NANOOSECONDS 值可以有效地禁止空闲线程在关闭之前终止。
默认情况下,保持活动策略仅在有超过 corePoolSize 个线程时适用,但方法 allowCoreThreadTimeOut(boolean) 也可用于将此超时策略应用于核心线程,只要 keepAliveTime 值不为零.
-
队列
任何 BlockingQueue 都可用于传输和保存提交的任务队列。此队列的使用与池大小交互:
- 如果线程池中正在运行的线程少于 corePoolSize,则 Executor将创建新线程执行任务而不是将任务添加到任务队列。
- 如果线程池中大于corePoolSize 的线程正在运行,Executor 总是将任务添加到队列中而不是创建新线程。
- 如果线程池中线程数大于corePoolSize、少于 maximumPoolSize、任务队列已满,则会创建一个新线程,
- 如果线程池中线程数大于maximumPoolSize、任务队列已满,在这种情况下,任务将被拒绝。
队列的一般形式:
- 直接交接。
工作队列的一个很好的默认选择是 SynchronousQueue,它将任务移交给线程而不用其他方式保留它们。 在这里,如果没有线程可立即运行,则将任务排队的尝试将失败,因此将构建一个新线程。 在处理可能具有内部依赖性的请求集时,此策略可避免锁定。 直接切换通常需要无限的maximumPoolSizes 以避免拒绝新提交的任务。 这反过来又承认了当命令平均持续到达速度快于它们可以处理的速度时无限线程增长的可能性。
- 无界队列。
使用无界队列(例如,没有预定义容量的 LinkedBlockingQueue)将导致新任务在所有 corePoolSize 线程都忙时在队列中等待。因此,不会创建超过 corePoolSize 的线程。 (因此maximumPoolSize的值没有任何影响。)当每个任务完全独立于其他任务时,这可能是合适的,因此任务不会影响彼此的执行;例如,在网页服务器中。虽然这种排队方式在平滑请求的瞬时爆发方面很有用,但它承认当命令的平均到达速度超过它们的处理速度时,工作队列可能会无限增长。
- 有界队列。
有界队列(例如,ArrayBlockingQueue)在与有限的 maximumPoolSizes 一起使用时有助于防止资源耗尽,但可能更难以调整和控制。队列大小和最大池大小可以相互权衡:使用大队列和小池可以最大限度地减少 CPU 使用率、操作系统资源和上下文切换开销,但会导致人为地降低吞吐量。如果任务频繁阻塞(例如,如果它们受 I/O 限制),则系统可能能够为比您允许的更多线程安排时间。使用小队列通常需要更大的池大小,这会使 CPU 更忙,但可能会遇到不可接受的调度开销,这也会降低吞吐量。
-
拒绝任务
当 Executor 已经关闭,并且当 Executor 对最大线程和工作队列容量使用有限边界并且饱和时,在方法 execute(Runnable) 中提交的新任务将被拒绝。 无论哪种情况,execute 方法都会调用其 RejectedExecutionHandler 的RejectedExecutionHandler.rejectedExecution(Runnable, ThreadPoolExecutor) 方法。提供了四个预定义的处理程序策略:
- 在默认的 ThreadPoolExecutor.AbortPolicy 中,处理程序在拒绝时抛出运行时 RejectedExecutionException。
- 在 ThreadPoolExecutor.CallerRunsPolicy 中,调用执行自身的线程运行任务。这提供了一个简单的反馈控制机制,可以减慢提交新任务的速度。
- 在 ThreadPoolExecutor.DiscardPolicy 中,无法执行的任务被简单地丢弃。此策略仅适用于从不依赖任务完成的极少数情况。
- 在 ThreadPoolExecutor.DiscardOldestPolicy 中,如果执行器没有关闭,工作队列头部的任务会被丢弃,然后重试执行(可能会再次失败,导致重复)。这种策略很少被接受。在几乎所有情况下,您还应该取消任务以在任何等待其完成的组件中导致异常,和/或记录失败,如 ThreadPoolExecutor.DiscardOldestPolicy 文档中所示。
可以定义和使用其他类型的 RejectedExecutionHandler 类。这样做需要小心,特别是当策略设计为仅在特定容量或排队策略下工作时。
-
Hook方法
此类提供受保护的可覆盖 beforeExecute(Thread, Runnable) 和 afterExecute(Runnable, Throwable) 方法,这些方法在每个任务执行之前和之后调用。这些可用于操作执行环境;例如,重新初始化 ThreadLocals、收集统计信息或添加日志条目。此外,可以覆盖已终止的方法以执行在 Executor 完全终止后需要完成的任何特殊处理。
如果钩子、回调或 BlockingQueue 方法抛出异常,内部工作线程可能会依次失败、突然终止并可能被替换。
-
队列维护
方法 getQueue() 允许访问工作队列以进行监视和调试。强烈建议不要将此方法用于任何其他目的。提供的两种方法 remove(Runnable) 和 purge 可用于在大量排队任务被取消时协助存储回收。
-
Reclamation
程序中不再引用并且没有剩余线程的池可以在不显式关闭的情况下被回收(垃圾收集)。您可以通过设置适当的保持活动时间、使用零核心线程的下限和/或设置 allowCoreThreadTimeOut(boolean) 来配置池以允许所有未使用的线程最终死亡。
package java.util.concurrent;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
*/
public class ThreadPoolExecutor extends AbstractExecutorService {
/**
* ctl 用来表示线程池到状态,包含两部分
* workerCount, 代表有效的线程数量
* runState, 代表当前线程池的状态running, shutting down等等
*
*
* runState 提供了线程池生命周期的状态。主要有以下取值:
*
* RUNNING: 接受新任务并处理任务队列中的任务
* SHUTDOWN: 不接受新任务,但是处理任务队列中的任务
* STOP: 不接受新任务,也不处理任务队列中的任务并且终端当前正在处理的任务
* TIDYING: 所有任务已经结束, workerCount==0,线程转化到TIDYING状态后将执行terminated() hook方法
* TERMINATED: terminated()方法已经执行完成
*
* 这些取值之间的数字顺序很重要,就可以直接用数字进行比较。 runState 单调递增,但并不是会经过每个状态。
* 这些状态之间的过渡是:
*
* RUNNING -> SHUTDOWN # 当前调用了shutdown()
*
* (RUNNING or SHUTDOWN) -> STOP # 当调用了shutdownNow()
*
* SHUTDOWN -> TIDYING # 当任务队列和线程池中线程都为空时
*
* STOP -> TIDYING # 当线程池为空时
*
* TIDYING -> TERMINATED # 当terminated() hook 方法执行完成时
*
* 当调用awaitTermination()后,会一直等待,直到线程池状态到达TERMINATED时返回
*
* 检测从 SHUTDOWN 到 TIDYING 的转换并不像您想要的那么简单,因为在 SHUTDOWN 状态期间队列可能会在非空之后变为空,
* 反之亦然,但是我们只能在看到它为空之后才能终止,我们看到 workerCount 是 0(有时需要重新检查——见下文)。
*/
//ctl用来表示状态和做线程数统计;int用2进制表示32位,前面3位表示状态,后面29位表示线程数
//这里在初始化对象时就已经设置了RUNNING,所以runState一开始就是RUNNING
//runState 表示线程池状态
//workerCount 表示线程池中线程数
//runState,workerCount这两个变量是不存在的,runState和workerCount两个变量一起组成了ctl
//前面3位表示runState,后面29位表示workerCount
//所以我们也可以认为runState,workerCount是存在的
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//COUNT_BITS表示位移量
private static final int COUNT_BITS = Integer.SIZE - 3;
//COUNT_MASK表示11111111111111111111111111111(29个1)
//ctl&COUNT_MASK就是只保留ctl低29位,结果也就是当前线程池中线程的数量
private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;
// 分别左移29位,也就是int后面29位都是0,用前面3位来表示runState
//它们的2进制和10进制分别时下面的值,可以看到它们的int值时从小到大排列的
// RUNNING = 11100000000000000000000000000000 --> -536870912
// SHUTDOWN = 0 --> 0
// STOP = 100000000000000000000000000000 --> 536870912
// TIDYING = 1000000000000000000000000000000 --> 1073741824
// TERMINATED = 1100000000000000000000000000000 --> 1610612736
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;
// ~COUNT_MASK表示将int前3位置1,后面29位置0;runStateOf根据传入的参数计算当前线程池的状态
private static int runStateOf(int c) { return c & ~COUNT_MASK; }
//workerCountOf根据传入的参数计算线程池中的线程数
private static int workerCountOf(int c) { return c & COUNT_MASK; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
//由于runState是int,通过比较int大小来确定它们的状态
private static boolean runStateLessThan(int c, int s) {
return c < s;
}
//由于runState是int,通过比较int大小来确定它们的状态
private static boolean runStateAtLeast(int c, int s) {
return c >= s;
}
//通过上面runState几种取值的定义,也能看到只有RUNNING这一种状态是小于SHUTDOWN的
private static boolean isRunning(int c) {
return c < SHUTDOWN;
}
//通过CAS的方式将ctl+1。用在创建一个线程后,增加线程的数量
private boolean compareAndIncrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect + 1);
}
//通过CAS的方式将ctl-1。用在一个线程结束后,减少线程的数量
private boolean compareAndDecrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect - 1);
}
//和上面的用法是一样的,ctl-1
private void decrementWorkerCount() {
ctl.addAndGet(-1);
}
/**
* The queue used for holding tasks and handing off to worker
* threads. We do not require that workQueue.poll() returning
* null necessarily means that workQueue.isEmpty(), so rely
* solely on isEmpty to see if the queue is empty (which we must
* do for example when deciding whether to transition from
* SHUTDOWN to TIDYING). This accommodates special-purpose
* queues such as DelayQueues for which poll() is allowed to
* return null even if it may later return non-null when delays
* expire.
*/
//用来保存任务的队列
private final BlockingQueue<Runnable> workQueue;
private final ReentrantLock mainLock = new ReentrantLock();
//当前线程池中所有线程集合
private final HashSet<Worker> workers = new HashSet<>();
//用来唤醒调用awaitTermination的挂起
private final Condition termination = mainLock.newCondition();
//线程池的线程从启动到现在曾经的最大线程数量。比如之前线程数是10,现在降到8,这个值还会10
private int largestPoolSize;
//完成任务的数量
private long completedTaskCount;
//线程工厂,用来产生新的线程
private volatile ThreadFactory threadFactory;
//当前的拒绝任务执行器
private volatile RejectedExecutionHandler handler;
//保活时间,如果当前线程数<corePoolSize,<=maximumPoolSize,会将空闲时间超过keepAliveTime的线程结束掉
//如果allowCoreThreadTimeOut==false,当线程数<corePoolSize,<=maximumPoolSize时,会将空闲时间超过keepAliveTime的线程结束掉
//如果allowCoreThreadTimeOut==true,将空闲时间超过keepAliveTime的线程结束掉
private volatile long keepAliveTime;
//是否允许当前线程数<corePoolSize,继续结束空闲时间超过keepAliveTime的线程
private volatile boolean allowCoreThreadTimeOut;
//核心线程数
private volatile int corePoolSize;
//最大线程数
private volatile int maximumPoolSize;
//默认的拒绝任务执行器
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();
private static final RuntimePermission shutdownPerm =
new RuntimePermission("modifyThread");
//用来执行任务的实体。实现了Runnable接口,代表一个可执行的线程。同时内部持有了一个Thread对象(用来真正执行任务的线程)
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
private static final long serialVersionUID = 6138294804551838833L;
//真正执行任务的线程
@SuppressWarnings("serial") // Unlikely to be serializable
final Thread thread;
//当前worker执行的第一个任务,有可能是null
@SuppressWarnings("serial") // Not statically typed as Serializable
Runnable firstTask;
//完成的任务数
volatile long completedTasks;
// TODO: switch to AbstractQueuedLongSynchronizer and move
// completedTasks into the lock word.
//根据传入的任务,创建对象并创建内部真正执行任务的线程,参数可能是null
Worker(Runnable firstTask) {
//这里将state=-1,这时是不能加锁的。因为加锁是判断state是不是等于0,等于0的场景下才能加锁成功。
//要执行lock方法,就需要先将state改成0。所以在runWorker方法中开始部分就会调用unlock将state设置成0.
//通过state==-1,不能执行加锁操作来禁用中断
//0代表当前未加锁,1代表当前加锁中
setState(-1);
//赋值第一个任务
this.firstTask = firstTask;
//通过线程工厂创建线程
this.thread = getThreadFactory().newThread(this);
}
public void run() {
runWorker(this);
}
//内部方法(主要是提供出来供子类复写),是否已经被加锁
protected boolean isHeldExclusively() {
return getState() != 0;
}
//内部方法(主要是提供出来供子类复写),尝试加锁,成功返回true,失败返回false
//注意:这里的锁是不可重入的。这里的入参也是没有使用到的。里面只是cas的方式将state从0设置成1
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); }
//尝试加锁,不阻塞。成功返回tue,失败返回false
public boolean tryLock() { return tryAcquire(1); }
//解锁
public void unlock() { release(1); }
//判断是否已经加锁
public boolean isLocked() { return isHeldExclusively(); }
//如果当前线程已经启动且当前线程没有被中断,就中断当前线程
void interruptIfStarted() {
Thread t;
//通过state>=0,表示已经执行了runWorker方法
//初始化的state==-1,在runWorker方法开始就会将state从-1设置成0,此后state不会小于0
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
//入参targetState取值是SHUTDOWN 或 STOP
//如果runState小于targetState,就将runState设置成targetState
private void advanceRunState(int targetState) {
// assert targetState == SHUTDOWN || targetState == STOP;
for (;;) {
int c = ctl.get();
if (runStateAtLeast(c, targetState) ||
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
break;
}
}
//如果(关闭和池和队列为空)或(停止和池为空),则转换到 TERMINATED 状态。
//如果有资格终止但 workerCount 非零,则中断空闲的工作程序以确保关闭信号传播。
//必须在任何可能使终止成为可能的操作之后调用此方法 - 在关闭期间减少工作线程数或从队列中删除任务。
//该方法是非私有的,允许从 ScheduledThreadPoolExecutor 访问。
final void tryTerminate() {
for (;;) {
int c = ctl.get();
//1.如果当前runState==RUNNING说明当前线程池正在运行,就直接返回
//2.runStateAtLeast(c, TIDYING) ,当前runState==TIDYING或者TERMINATED,不需要处理
//3.runStateLessThan(c, STOP) 这个条件成立的runState取值只有RUNNING和SHUTDOWN,RUNNING在前面已经处理
//所以这里的runState就只有SHUTDOWN,所以也就是如果当前状态是SHUTDOWN且队列中还有任务,也不处理
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateLessThan(c, STOP) && ! workQueue.isEmpty()))
return;
//能走到下面,说明当前runState==SHUTDOWN或者runState==STOP
//如果当前还有线程存活,就中断一个线程
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
}
//能走到下面,说明当前runState==SHUTDOWN或者runState==STOP,且当前线程存活数==0
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//将runState设置成TIDYING
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
//这个方法是留给子类去实现的,子类可以在这个方法中执行线程池停止后的清理工作
terminated();
} finally {
//将runState设置成TERMINATED
ctl.set(ctlOf(TERMINATED, 0));
//唤醒在awaitTermination方法上的挂起
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
private void checkShutdownAccess() {
// assert mainLock.isHeldByCurrentThread();
@SuppressWarnings("removal")
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(shutdownPerm);
for (Worker w : workers)
security.checkAccess(w.thread);
}
}
//中断所有的线程
private void interruptWorkers() {
// assert mainLock.isHeldByCurrentThread();
for (Worker w : workers)
w.interruptIfStarted();
}
//中断空闲的线程
//参数onlyOne==true表示中断一个线程
//参数onlyOne==false表示中断所有线程
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
//首先判断当前线程是否已经被中断,如果已经中断就再执行中断
//w.tryLock()是判断当前线程是否在执行任务(每次在获取任务后会执行lock,任务执行完后会执行unlock),
//tryLock方法执行成功说明线程当前空闲中(在等待获取任务),这时就执行中断
//注意:onlyOne在这个if外面,就说明有可能一个中断都没执行,onlyOne==false时,也可能一个中断都没执行
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
//中断一个空闲的线程
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
//是否中断一个线程
private static final boolean ONLY_ONE = true;
//将任务交给拒绝任务执行器去处理
final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
//在调用关闭时运行状态转换后执行任何进一步的清理。 此处无操作,但被 ScheduledThreadPoolExecutor 用于取消延迟任务。
void onShutdown() {
}
//将任务队列中的任务转移出去
private List<Runnable> drainQueue() {
BlockingQueue<Runnable> q = workQueue;
ArrayList<Runnable> taskList = new ArrayList<>();
q.drainTo(taskList);
if (!q.isEmpty()) {
for (Runnable r : q.toArray(new Runnable[0])) {
if (q.remove(r))
taskList.add(r);
}
}
return taskList;
}
//添加一个线程到线程池,firstTask是新创建线程的第一个任务,core表示当前添加的是否是核心线程
//返回的结果表示新增线程是否成功
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (int c = ctl.get();;) {
//这个条件写的比较绕:主要也就是两种场景
//1.runState==SHUTDOWN条件下,firstTask!=null或者任务队列是空的,就会直接返回false
//2.runState>=STOP条件下,这种场景下就直接返回false
if (runStateAtLeast(c, SHUTDOWN)
&& (runStateAtLeast(c, STOP)
|| firstTask != null
|| workQueue.isEmpty()))
return false;
for (;;) {
//这个if主要是根据我们传入的core来判断当前线程池的线程数
if (workerCountOf(c)
>= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
return false;
//将线程数计数+1,在多线程场景下有可能失败,所以需要重新获取c,重新执行
if (compareAndIncrementWorkerCount(c))
//走到这里,就会直接跳出双层循环
break retry;
c = ctl.get(); // Re-read ctl
//如果runState不是RUNNING,就又回回到上面去重新开始,根据实际情况从上面两个return false的地方返回
if (runStateAtLeast(c, SHUTDOWN))
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) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//这里会再次获取runState
int c = ctl.get();
//判断线程池的状态,
//1.runState==RUNNING
//2.runState==SHUTDOWN且firstTask==null就会去执行后续操作
if (isRunning(c) ||
(runStateLessThan(c, STOP) && firstTask == null)) {
//这里会对线程的状态进行判断,线程在执行start方法之前的状态== Thread.State.NEW
if (t.getState() != Thread.State.NEW)
throw new IllegalThreadStateException();
//将创建的线程的载体加到线程池中
workers.add(w);
//标记线程增加成功
workerAdded = true;
int s = workers.size();
//更新线程数量
if (s > largestPoolSize)
largestPoolSize = s;
}
} finally {
mainLock.unlock();
}
//如果线程添加成功,就启动线程,标记线程启动成功
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
//如果线程启动没有标记为true,说明线程增加没有成功,后续就需要执行对应的清理工作
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
//这里就会对增加线程失败进行处理
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//从线程队列里面删除线程,这个remove 不存在也不会报错
if (w != null)
workers.remove(w);
//线程计数-1
decrementWorkerCount();
//因为有可能是runState变化,导致添加线程失败,所以这里需要runState去判断是否能结束线程池
tryTerminate();
} finally {
mainLock.unlock();
}
}
//这个方法只会在runWorker中调用
//completedAbruptly表示是不是执行过程中出现异常导致线程结束。true表示出现异常,false表示正常原因结束(getTask()返回值是null)
private void processWorkerExit(Worker w, boolean completedAbruptly) {
//如果是异常原因导致线程退出,之前线程计数中没有进行-1操作,这里就需要补操作
//对于正常原因导致线程退出的,这个-1操作已经在getTask()中执行了
if (completedAbruptly)
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//将退出线程完成的任务数加到总完成任务数里面
completedTaskCount += w.completedTasks;
//从线程集合中移除线程
workers.remove(w);
} finally {
mainLock.unlock();
}
//尝试去结束线程池
tryTerminate();
int c = ctl.get();
//如果runState==RUNNING或SHUTDOWN,就会去根据实际需要增加线程
if (runStateLessThan(c, STOP)) {
//如果线程是正常结束,判断线程池允许的最小线程数
//如果允许核心线程超时,那最小线程数就是0;否则就是corePoolSize
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
//如果任务队列中还有任务,那最小线程数就需要大于0
if (min == 0 && ! workQueue.isEmpty())
min = 1;
//如果当前线程大于最小需求量,那就不需要增加线程数,直接返回
if (workerCountOf(c) >= min)
return; // replacement not needed
}
//作为非核心线程加到线程池中
addWorker(null, false);
}
}
//在这里就会从任务队列中获取任务,如果返回null,那就是对应线程需要正常退出了
private Runnable getTask() {
//表示从任务队列中获取数据的时候是否已经超时
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
// Check if queue empty only if necessary.
//这里有两个条件
//1.当前状态是SHUTDOWN,任务队列为空,工作线程数-1,这里直接返回null
//2.当前状态>=STOP,这时不会去判断任务队列,工作线程数-1,直接返回null
//这里也重点体现了SHUTDOWN和STOP的区别,
//对于SHUTDOWN,还会继续从任务队列中获取任务;STOP就不会从任务队列中获取任务,直接从这里返回null
if (runStateAtLeast(c, SHUTDOWN)
&& (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
//获取到当前线程数
int wc = workerCountOf(c);
// Are workers subject to culling?
//这里会判断当前线程是否允许超时,这里也有两种场景
//1.如果设置了允许核心线程超时,那就表示所有的线程都会超时,当前线程就允许超时
//2.如果当前线程池中线程数>核心线程数,当前线程也允许超时
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//1.如果当前线程数大于了最大线程数,这时就要减少线程数,直到降低到等于maximumPoolSize大小
//2.如果当前线程允许超时,且已经超时
//在上面两种情况下,还需要判断线程数>0或者任务队列为空,这时才会去减少线程。
//主要是为了避免,任务队列中有任务,但是当前线程池已经没有线程了这种情况
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
//这里就是从任务队列中获取任务了,如果当前线程允许超时,那就通过poll的方式来获取,要不返回获取的任务,要不返回null
//如果当前线程不允许超时,那就通过take使用阻塞的方式来获取任务
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
//如果获取到了任务,就会从这里返回任务,后续去对获取到的任务进行处理
if (r != null)
return r;
//走到这里说明没有获取到任务,是由于poll时间到达,返回了null
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
//这个是执行任务的主方法
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock();
boolean completedAbruptly = true;
try {
//这里会循环通过getTask来获取任务后调用任务的run方法来执行
//如果getTask返回是null,那说明当前线程需要结束,后面就会结束当前线程
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
//1.判断当前runState>=STOP,当前线程没有被中断,则需要中断当前线程
//2.如果当前线程已经被中断且当前runState>=STOP,Thread.interrupted()这个会清除当前的中断标记
//上面两种条件都成立的情况下,这时就要去中断当前线程
//注意:我们从这里也能看到如果当前runState<STOP,这时由于调用了Thread.interrupted(),就只会清除当前的中断标记
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
//这个是留给子类去扩展的
beforeExecute(wt, task);
try {
//调用任务的run方法去执行任务
task.run();
//留给子类去扩展
afterExecute(task, null);
} catch (Throwable ex) {
afterExecute(task, ex);
throw ex;
}
} finally {
task = null;
//当前线程执行的任务书+1
w.completedTasks++;
w.unlock();
}
}
//这个没有在finally类面,没有抛出异常才会走到这里
//通过这个参数判断当前线程是由于没有了任务正常结束,还是由于抛出异常走到这里
//对于异常原因,processWorkerExit方法中会让线程数-1,同时会重新创建一个新的线程
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
// Public constructors and methods
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters, the
* {@linkplain Executors#defaultThreadFactory default thread factory}
* and the {@linkplain ThreadPoolExecutor.AbortPolicy
* default rejected execution handler}.
*
* <p>It may be more convenient to use one of the {@link Executors}
* factory methods instead of this general purpose constructor.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters and the {@linkplain ThreadPoolExecutor.AbortPolicy
* default rejected execution handler}.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters and the
* {@linkplain Executors#defaultThreadFactory default thread factory}.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code handler} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
/**
* Executes the given task sometime in the future. The task
* may execute in a new thread or in an existing pooled thread.
*
* If the task cannot be submitted for execution, either because this
* executor has been shutdown or because its capacity has been reached,
* the task is handled by the current {@link RejectedExecutionHandler}.
*
* @param command the task to execute
* @throws RejectedExecutionException at discretion of
* {@code RejectedExecutionHandler}, if the task
* cannot be accepted for execution
* @throws NullPointerException if {@code command} is null
*/
//这个是我们外部代码调用的入口
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
//1.如果少于 corePoolSize 线程正在运行,尝试以给定的任务作为第一个启动一个新线程任务。
//对 addWorker 的调用以原子方式检查 runState 和workerCount,因此可以防止会增加的误报不应该线程时,返回 false。
//2.如果一个任务可以添加到任务队列,那么还需要检查是否应该添加一个线程(因为现有的自上次检查后死亡)或线程池在进入此方法后
//关闭。 所以需要重新检查状态,并在必要时回滚入队,如果停止,如果没有,则启动一个新线程。
//3.如果任务不能添加到任务队列中,那么尝试添加一个新的线。 如果它失败了,那么说明已经关闭或饱和了所以拒绝任务。
int c = ctl.get();
//这里就是上面说的第1种情况,
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//这里说的是第2中情况
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//这里对线程池的状态进行了重新判断,确保将任务添加到任务队列中整个过程线程池的状态都是RUNNING
//如果将任务添加到任务队列后,发现线程池状态已经不是RUNNING了,这时就需要将任务从任务队列中移除掉
if (! isRunning(recheck) && remove(command))
//交给RejectedExecutionHandler去处理
reject(command);
//任务添加到任务队列了,但是当前线程池中没有线程了,这时就要新创建一个线程
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//第3中情况,核心线程数满了,任务队列也满了,就会走到这里,去创建个新线程去执行任务,
//如果当前线程池中的线程数已经>=maximumPoolSize,addWorker也会返回false
else if (!addWorker(command, false))
reject(command);
}
/**
* Initiates an orderly shutdown in which previously submitted
* tasks are executed, but no new tasks will be accepted.
* Invocation has no additional effect if already shut down.
*
* <p>This method does not wait for previously submitted tasks to
* complete execution. Use {@link #awaitTermination awaitTermination}
* to do that.
*
* @throws SecurityException {@inheritDoc}
*/
//shutdown和shutdownNow两个方法代码基本是一样的,只不过一个是将runState设置成SHUTDOWN,一个是设置成STOP
//SHUTDOWN和STOP都不会接收提交给线程池的任务,区别是STOP不会运行继续执行任务队列的任务,而SHUTDOWN将继续执行任务队列中的任务
//SHUTDOWN只会中断当前没有执行任务的线程,而STOP会中断所有的线程
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
//设置当前线程池状态为SHUTDOWN
advanceRunState(SHUTDOWN);
//尝试去中断所有空闲的线程,
//interruptIdleWorkers方法中会首先尝试调用worker.tryLock()方法,成功后就会去中断对应线程
//worker类在runWorker方法执行任务先后会分别调用worker.lock,worker.unlock。
//所以这里只会中断没有执行任务的线程
interruptIdleWorkers();
//留给子类去实现
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
//尝试去结束线程池
tryTerminate();
}
/**
* Attempts to stop all actively executing tasks, halts the
* processing of waiting tasks, and returns a list of the tasks
* that were awaiting execution. These tasks are drained (removed)
* from the task queue upon return from this method.
*
* <p>This method does not wait for actively executing tasks to
* terminate. Use {@link #awaitTermination awaitTermination} to
* do that.
*
* <p>There are no guarantees beyond best-effort attempts to stop
* processing actively executing tasks. This implementation
* interrupts tasks via {@link Thread#interrupt}; any task that
* fails to respond to interrupts may never terminate.
*
* @throws SecurityException {@inheritDoc}
*/
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
//这里和上面的shutdown方法也有区别,这里会中断所有的线程
interruptWorkers();
//STOP状态不会执行任务队列中剩余任务,这里会将剩余任务返回
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
public boolean isShutdown() {
return runStateAtLeast(ctl.get(), SHUTDOWN);
}
/** Used by ScheduledThreadPoolExecutor. */
boolean isStopped() {
return runStateAtLeast(ctl.get(), STOP);
}
/**
* Returns true if this executor is in the process of terminating
* after {@link #shutdown} or {@link #shutdownNow} but has not
* completely terminated. This method may be useful for
* debugging. A return of {@code true} reported a sufficient
* period after shutdown may indicate that submitted tasks have
* ignored or suppressed interruption, causing this executor not
* to properly terminate.
*
* @return {@code true} if terminating but not yet terminated
*/
//只有RUNNING是运行的状态,其他状态除了TERMINATED都可以认为是中间的过度状态
//这里就通过runState来判断线程池是否在停止中,但是还没有完全停止
public boolean isTerminating() {
int c = ctl.get();
return runStateAtLeast(c, SHUTDOWN) && runStateLessThan(c, TERMINATED);
}
public boolean isTerminated() {
return runStateAtLeast(ctl.get(), TERMINATED);
}
//这个是等待当前线程池结束
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
while (runStateLessThan(ctl.get(), TERMINATED)) {
if (nanos <= 0L)
return false;
//这里通过Condition来实现的等待,如果线程池结束,tryTerminate方法最后就会调用signalAll唤醒这里
nanos = termination.awaitNanos(nanos);
}
return true;
} finally {
mainLock.unlock();
}
}
// Override without "throws Throwable" for compatibility with subclasses
// whose finalize method invokes super.finalize() (as is recommended).
// Before JDK 11, finalize() had a non-empty method body.
/**
* @implNote Previous versions of this class had a finalize method
* that shut down this executor, but in this version, finalize
* does nothing.
*/
@Deprecated(since="9")
protected void finalize() {}
/**
* Sets the thread factory used to create new threads.
*
* @param threadFactory the new thread factory
* @throws NullPointerException if threadFactory is null
* @see #getThreadFactory
*/
public void setThreadFactory(ThreadFactory threadFactory) {
if (threadFactory == null)
throw new NullPointerException();
this.threadFactory = threadFactory;
}
/**
* Returns the thread factory used to create new threads.
*
* @return the current thread factory
* @see #setThreadFactory(ThreadFactory)
*/
public ThreadFactory getThreadFactory() {
return threadFactory;
}
/**
* Sets a new handler for unexecutable tasks.
*
* @param handler the new handler
* @throws NullPointerException if handler is null
* @see #getRejectedExecutionHandler
*/
public void setRejectedExecutionHandler(RejectedExecutionHandler handler) {
if (handler == null)
throw new NullPointerException();
this.handler = handler;
}
/**
* Returns the current handler for unexecutable tasks.
*
* @return the current handler
* @see #setRejectedExecutionHandler(RejectedExecutionHandler)
*/
public RejectedExecutionHandler getRejectedExecutionHandler() {
return handler;
}
/**
* Sets the core number of threads. This overrides any value set
* in the constructor. If the new value is smaller than the
* current value, excess existing threads will be terminated when
* they next become idle. If larger, new threads will, if needed,
* be started to execute any queued tasks.
*
* @param corePoolSize the new core size
* @throws IllegalArgumentException if {@code corePoolSize < 0}
* or {@code corePoolSize} is greater than the {@linkplain
* #getMaximumPoolSize() maximum pool size}
* @see #getCorePoolSize
*/
public void setCorePoolSize(int corePoolSize) {
if (corePoolSize < 0 || maximumPoolSize < corePoolSize)
throw new IllegalArgumentException();
int delta = corePoolSize - this.corePoolSize;
this.corePoolSize = corePoolSize;
if (workerCountOf(ctl.get()) > corePoolSize)
interruptIdleWorkers();
else if (delta > 0) {
// We don't really know how many new threads are "needed".
// As a heuristic, prestart enough new workers (up to new
// core size) to handle the current number of tasks in
// queue, but stop if queue becomes empty while doing so.
int k = Math.min(delta, workQueue.size());
while (k-- > 0 && addWorker(null, true)) {
if (workQueue.isEmpty())
break;
}
}
}
/**
* Returns the core number of threads.
*
* @return the core number of threads
* @see #setCorePoolSize
*/
public int getCorePoolSize() {
return corePoolSize;
}
/**
* Starts a core thread, causing it to idly wait for work. This
* overrides the default policy of starting core threads only when
* new tasks are executed. This method will return {@code false}
* if all core threads have already been started.
*
* @return {@code true} if a thread was started
*/
public boolean prestartCoreThread() {
return workerCountOf(ctl.get()) < corePoolSize &&
addWorker(null, true);
}
/**
* Same as prestartCoreThread except arranges that at least one
* thread is started even if corePoolSize is 0.
*/
void ensurePrestart() {
int wc = workerCountOf(ctl.get());
if (wc < corePoolSize)
addWorker(null, true);
else if (wc == 0)
addWorker(null, false);
}
/**
* Starts all core threads, causing them to idly wait for work. This
* overrides the default policy of starting core threads only when
* new tasks are executed.
*
* @return the number of threads started
*/
public int prestartAllCoreThreads() {
int n = 0;
while (addWorker(null, true))
++n;
return n;
}
/**
* Returns true if this pool allows core threads to time out and
* terminate if no tasks arrive within the keepAlive time, being
* replaced if needed when new tasks arrive. When true, the same
* keep-alive policy applying to non-core threads applies also to
* core threads. When false (the default), core threads are never
* terminated due to lack of incoming tasks.
*
* @return {@code true} if core threads are allowed to time out,
* else {@code false}
*
* @since 1.6
*/
public boolean allowsCoreThreadTimeOut() {
return allowCoreThreadTimeOut;
}
/**
* Sets the policy governing whether core threads may time out and
* terminate if no tasks arrive within the keep-alive time, being
* replaced if needed when new tasks arrive. When false, core
* threads are never terminated due to lack of incoming
* tasks. When true, the same keep-alive policy applying to
* non-core threads applies also to core threads. To avoid
* continual thread replacement, the keep-alive time must be
* greater than zero when setting {@code true}. This method
* should in general be called before the pool is actively used.
*
* @param value {@code true} if should time out, else {@code false}
* @throws IllegalArgumentException if value is {@code true}
* and the current keep-alive time is not greater than zero
*
* @since 1.6
*/
public void allowCoreThreadTimeOut(boolean value) {
if (value && keepAliveTime <= 0)
throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
if (value != allowCoreThreadTimeOut) {
allowCoreThreadTimeOut = value;
if (value)
interruptIdleWorkers();
}
}
/**
* Sets the maximum allowed number of threads. This overrides any
* value set in the constructor. If the new value is smaller than
* the current value, excess existing threads will be
* terminated when they next become idle.
*
* @param maximumPoolSize the new maximum
* @throws IllegalArgumentException if the new maximum is
* less than or equal to zero, or
* less than the {@linkplain #getCorePoolSize core pool size}
* @see #getMaximumPoolSize
*/
public void setMaximumPoolSize(int maximumPoolSize) {
if (maximumPoolSize <= 0 || maximumPoolSize < corePoolSize)
throw new IllegalArgumentException();
this.maximumPoolSize = maximumPoolSize;
if (workerCountOf(ctl.get()) > maximumPoolSize)
interruptIdleWorkers();
}
/**
* Returns the maximum allowed number of threads.
*
* @return the maximum allowed number of threads
* @see #setMaximumPoolSize
*/
public int getMaximumPoolSize() {
return maximumPoolSize;
}
/**
* Sets the thread keep-alive time, which is the amount of time
* that threads may remain idle before being terminated.
* Threads that wait this amount of time without processing a
* task will be terminated if there are more than the core
* number of threads currently in the pool, or if this pool
* {@linkplain #allowsCoreThreadTimeOut() allows core thread timeout}.
* This overrides any value set in the constructor.
*
* @param time the time to wait. A time value of zero will cause
* excess threads to terminate immediately after executing tasks.
* @param unit the time unit of the {@code time} argument
* @throws IllegalArgumentException if {@code time} less than zero or
* if {@code time} is zero and {@code allowsCoreThreadTimeOut}
* @see #getKeepAliveTime(TimeUnit)
*/
public void setKeepAliveTime(long time, TimeUnit unit) {
if (time < 0)
throw new IllegalArgumentException();
if (time == 0 && allowsCoreThreadTimeOut())
throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
long keepAliveTime = unit.toNanos(time);
long delta = keepAliveTime - this.keepAliveTime;
this.keepAliveTime = keepAliveTime;
if (delta < 0)
interruptIdleWorkers();
}
/**
* Returns the thread keep-alive time, which is the amount of time
* that threads may remain idle before being terminated.
* Threads that wait this amount of time without processing a
* task will be terminated if there are more than the core
* number of threads currently in the pool, or if this pool
* {@linkplain #allowsCoreThreadTimeOut() allows core thread timeout}.
*
* @param unit the desired time unit of the result
* @return the time limit
* @see #setKeepAliveTime(long, TimeUnit)
*/
public long getKeepAliveTime(TimeUnit unit) {
return unit.convert(keepAliveTime, TimeUnit.NANOSECONDS);
}
/* User-level queue utilities */
/**
* Returns the task queue used by this executor. Access to the
* task queue is intended primarily for debugging and monitoring.
* This queue may be in active use. Retrieving the task queue
* does not prevent queued tasks from executing.
*
* @return the task queue
*/
public BlockingQueue<Runnable> getQueue() {
return workQueue;
}
/**
* Removes this task from the executor's internal queue if it is
* present, thus causing it not to be run if it has not already
* started.
*
* <p>This method may be useful as one part of a cancellation
* scheme. It may fail to remove tasks that have been converted
* into other forms before being placed on the internal queue.
* For example, a task entered using {@code submit} might be
* converted into a form that maintains {@code Future} status.
* However, in such cases, method {@link #purge} may be used to
* remove those Futures that have been cancelled.
*
* @param task the task to remove
* @return {@code true} if the task was removed
*/
public boolean remove(Runnable task) {
boolean removed = workQueue.remove(task);
tryTerminate(); // In case SHUTDOWN and now empty
return removed;
}
/**
* Tries to remove from the work queue all {@link Future}
* tasks that have been cancelled. This method can be useful as a
* storage reclamation operation, that has no other impact on
* functionality. Cancelled tasks are never executed, but may
* accumulate in work queues until worker threads can actively
* remove them. Invoking this method instead tries to remove them now.
* However, this method may fail to remove tasks in
* the presence of interference by other threads.
*/
public void purge() {
final BlockingQueue<Runnable> q = workQueue;
try {
Iterator<Runnable> it = q.iterator();
while (it.hasNext()) {
Runnable r = it.next();
if (r instanceof Future<?> && ((Future<?>)r).isCancelled())
it.remove();
}
} catch (ConcurrentModificationException fallThrough) {
// Take slow path if we encounter interference during traversal.
// Make copy for traversal and call remove for cancelled entries.
// The slow path is more likely to be O(N*N).
for (Object r : q.toArray())
if (r instanceof Future<?> && ((Future<?>)r).isCancelled())
q.remove(r);
}
tryTerminate(); // In case SHUTDOWN and now empty
}
/* Statistics */
/**
* Returns the current number of threads in the pool.
*
* @return the number of threads
*/
public int getPoolSize() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Remove rare and surprising possibility of
// isTerminated() && getPoolSize() > 0
return runStateAtLeast(ctl.get(), TIDYING) ? 0
: workers.size();
} finally {
mainLock.unlock();
}
}
/**
* Returns the approximate number of threads that are actively
* executing tasks.
*
* @return the number of threads
*/
public int getActiveCount() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
int n = 0;
for (Worker w : workers)
if (w.isLocked())
++n;
return n;
} finally {
mainLock.unlock();
}
}
/**
* Returns the largest number of threads that have ever
* simultaneously been in the pool.
*
* @return the number of threads
*/
public int getLargestPoolSize() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
return largestPoolSize;
} finally {
mainLock.unlock();
}
}
/**
* Returns the approximate total number of tasks that have ever been
* scheduled for execution. Because the states of tasks and
* threads may change dynamically during computation, the returned
* value is only an approximation.
*
* @return the number of tasks
*/
public long getTaskCount() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
long n = completedTaskCount;
for (Worker w : workers) {
n += w.completedTasks;
if (w.isLocked())
++n;
}
return n + workQueue.size();
} finally {
mainLock.unlock();
}
}
/**
* Returns the approximate total number of tasks that have
* completed execution. Because the states of tasks and threads
* may change dynamically during computation, the returned value
* is only an approximation, but one that does not ever decrease
* across successive calls.
*
* @return the number of tasks
*/
public long getCompletedTaskCount() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
long n = completedTaskCount;
for (Worker w : workers)
n += w.completedTasks;
return n;
} finally {
mainLock.unlock();
}
}
/**
* Returns a string identifying this pool, as well as its state,
* including indications of run state and estimated worker and
* task counts.
*
* @return a string identifying this pool, as well as its state
*/
public String toString() {
long ncompleted;
int nworkers, nactive;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
ncompleted = completedTaskCount;
nactive = 0;
nworkers = workers.size();
for (Worker w : workers) {
ncompleted += w.completedTasks;
if (w.isLocked())
++nactive;
}
} finally {
mainLock.unlock();
}
int c = ctl.get();
String runState =
isRunning(c) ? "Running" :
runStateAtLeast(c, TERMINATED) ? "Terminated" :
"Shutting down";
return super.toString() +
"[" + runState +
", pool size = " + nworkers +
", active threads = " + nactive +
", queued tasks = " + workQueue.size() +
", completed tasks = " + ncompleted +
"]";
}
/* Extension hooks */
/**
* Method invoked prior to executing the given Runnable in the
* given thread. This method is invoked by thread {@code t} that
* will execute task {@code r}, and may be used to re-initialize
* ThreadLocals, or to perform logging.
*
* <p>This implementation does nothing, but may be customized in
* subclasses. Note: To properly nest multiple overridings, subclasses
* should generally invoke {@code super.beforeExecute} at the end of
* this method.
*
* @param t the thread that will run task {@code r}
* @param r the task that will be executed
*/
protected void beforeExecute(Thread t, Runnable r) { }
/**
* Method invoked upon completion of execution of the given Runnable.
* This method is invoked by the thread that executed the task. If
* non-null, the Throwable is the uncaught {@code RuntimeException}
* or {@code Error} that caused execution to terminate abruptly.
*
* <p>This implementation does nothing, but may be customized in
* subclasses. Note: To properly nest multiple overridings, subclasses
* should generally invoke {@code super.afterExecute} at the
* beginning of this method.
*
* <p><b>Note:</b> When actions are enclosed in tasks (such as
* {@link FutureTask}) either explicitly or via methods such as
* {@code submit}, these task objects catch and maintain
* computational exceptions, and so they do not cause abrupt
* termination, and the internal exceptions are <em>not</em>
* passed to this method. If you would like to trap both kinds of
* failures in this method, you can further probe for such cases,
* as in this sample subclass that prints either the direct cause
* or the underlying exception if a task has been aborted:
*
* <pre> {@code
* class ExtendedExecutor extends ThreadPoolExecutor {
* // ...
* protected void afterExecute(Runnable r, Throwable t) {
* super.afterExecute(r, t);
* if (t == null
* && r instanceof Future<?>
* && ((Future<?>)r).isDone()) {
* try {
* Object result = ((Future<?>) r).get();
* } catch (CancellationException ce) {
* t = ce;
* } catch (ExecutionException ee) {
* t = ee.getCause();
* } catch (InterruptedException ie) {
* // ignore/reset
* Thread.currentThread().interrupt();
* }
* }
* if (t != null)
* System.out.println(t);
* }
* }}</pre>
*
* @param r the runnable that has completed
* @param t the exception that caused termination, or null if
* execution completed normally
*/
protected void afterExecute(Runnable r, Throwable t) { }
/**
* Method invoked when the Executor has terminated. Default
* implementation does nothing. Note: To properly nest multiple
* overridings, subclasses should generally invoke
* {@code super.terminated} within this method.
*/
protected void terminated() { }
/* Predefined RejectedExecutionHandlers */
/**
* A handler for rejected tasks that runs the rejected task
* directly in the calling thread of the {@code execute} method,
* unless the executor has been shut down, in which case the task
* is discarded.
*/
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() { }
/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
/**
* A handler for rejected tasks that throws a
* {@link RejectedExecutionException}.
*
* This is the default handler for {@link ThreadPoolExecutor} and
* {@link ScheduledThreadPoolExecutor}.
*/
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an {@code AbortPolicy}.
*/
public AbortPolicy() { }
/**
* Always throws RejectedExecutionException.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException always
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
/**
* A handler for rejected tasks that silently discards the
* rejected task.
*/
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }
/**
* Does nothing, which has the effect of discarding task r.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
/**
* A handler for rejected tasks that discards the oldest unhandled
* request and then retries {@code execute}, unless the executor
* is shut down, in which case the task is discarded. This policy is
* rarely useful in cases where other threads may be waiting for
* tasks to terminate, or failures must be recorded. Instead consider
* using a handler of the form:
* <pre> {@code
* new RejectedExecutionHandler() {
* public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
* Runnable dropped = e.getQueue().poll();
* if (dropped instanceof Future<?>) {
* ((Future<?>)dropped).cancel(false);
* // also consider logging the failure
* }
* e.execute(r); // retry
* }}}</pre>
*/
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() { }
/**
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
}
-
总结
-
线程池的状态
-
线程池的状态runState这个不存在的变量来表示的。线程池创建之初,runState就是RUNNING,
这是通过
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
这句来设置的,初始化状态是RUNNING,线程数是0状态都是int类型,故它们的大小关系是RUNNING < SHUTDOWN < STOP < TIDYING < TERMINATED
-
这几种状态之间的转换关系是这样的:
-
初始状态是RUNNING,最终状态是TERMINATED
-
在调用了
shutdown
方法后状态会变成SHUTDOWN,调用了shutdownNow
方法后状态变成shutdownNow,在这两个方法中,都会 调用到tryTerminate
,当线程数==0是,状态变成TIDYING;调用完terminated
后,变成最终状态TERMINATED![image-20211113124113720](
-
-
-
image-20211113124113720.png)
+ SHUTDOWN和STOP的区别
这两个状态比较相近,但是它们还是很大区别的
1. SHUTDOWN会继续执行任务队列中的任务,STOP不会执行任务队列中的任务。这个主要提交在`getTask`方法中
2. 在当前线程结束时,SHUTDOWN会根据情况去判断是否新创建一个线程。STOP不会进行此操作。这个主要体现在`processWorkerExit`方法中
2. SHUTDOWN只会中断当前没有执行任务的线程(体现在`shutdown`方法中调用的是`interruptIdleWorkers`),而STOP会中断所有的线程(体现在`shutdownNow`中调用的是`interruptWorkers`)
- 关于中断
我们能看到线程池实现中大量使用了线程中断。线程中断的知识不多,但是很重要。具体可以看看我之前关于中断的博文关于Thread的interrupt
线程池中中断主要时配合runState状态的变化,来设置对应线程的中断,使对应线程能够感知到对应中断,做出对应的调整。
-
对于提交给线程池的任务
对于提交给线程池的任务,线程池是不会帮我们去停止的,线程池唯一能做的就是设置我们执行任务的线程中断。我们我们的任务中可以通过会抛出中断异常的方法或者手动判断当前线程是否已经被中断来响应线程池状态的变化。
需要注意的第一是如果我们提交到线程池的任务一直结束不了,这会阻塞线程池关闭的
像下面的例子,由于我们提交的任务一直无法结束,且我们的任务没有对中断进行合适的响应,就会由于我们的任务一直在运行,阻止线程池结束。
import java.time.LocalDateTime; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class InterrruptDemo { public static void main(String[] args) throws InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(1); executorService.execute(() -> { while (true) { System.out.println(Thread.currentThread().getName() + " "+LocalDateTime.now()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread.sleep(1000); executorService.shutdownNow(); while (!executorService.isTerminated()) { Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + " 线程池当前没有完全关闭"); } System.out.println(Thread.currentThread().getName() + " 线程池已经完全关闭"); } }
像下面这样,就可以正确响应线程池状态的变化,在线程池关闭的时候,结束我们的任务
import java.time.LocalDateTime; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class InterrruptDemo { public static void main(String[] args) throws InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(1); executorService.execute(() -> { try { while (true) { System.out.println(Thread.currentThread().getName() + " " + LocalDateTime.now()); Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } }); Thread.sleep(1000); executorService.shutdownNow(); while (!executorService.isTerminated()) { Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + " 线程池当前没有完全关闭"); } System.out.println(Thread.currentThread().getName() + " 线程池已经完全关闭"); } }