九、线程池 Executor
1、Java线程理解
线程是调度CPU的最小单元,也叫轻量级进程 LWP( Light Weight Process)
小拓展:两种线程模型:用户级线程(ULT)、内核级线程(KLT)
用户线程(ULT)
:用户程序实现,不依赖操作系统核心,应用提供创建、同步、调度和管理线程的函数来控制用户线程。不需要用户态/内核态切换,速度快
。内核对ULT无感知,线程阻塞则进程(包括它的所有线程)阻塞。
内核线程(KLT)
:系统内核管理线程(KLT),内核保存线程的状态和上下文信息,线程阻塞不会引起进程阻塞。在多处理器系统上,多线程在多处理器上并行运行。线程的创建、调度和管理由内核完成,效率比ULT要慢,比进程操作快。JVM 用的是 KLT!
2、线程池的意义
线程是稀缺资源,它的创建与销毁是一个相对偏重且耗资源的操作,而 Java 线程依赖于内核线程,创建线程需要进行 操作系统状态切换
,为避免资源过度消耗需要设法 重用线程
执行多个任务。线程池就是一个线程缓存,负责对线程进行统一分配、调优与监控。
▼什么时候使用线程池?
- 单个任务处理时间比较短
- 需要处理的任务数量很大
▼线程池优势
- 重用存在的线程,减少线程创建、销毁的开销,提高性能
- 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行
- 提高线程的可管理性,可统一分配、调优和监控
3、Executor
-
背景:经常创建、销毀、使用量特别大的资源,比如并发情况下的线程,对性能影响很大
-
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中可以避免频繁创建、销毀,实现重复利用。类似生活中的公共交通工具
-
好处
- 提高响应速度(减少了创建新线程的时间)
- 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
- 便于线程管理,核心参数如下:
- corePoolSize:核心线程数
- maximumPoolsize:最大线程数(核心线程 + 非核心线程)
- keepAliveTime:非核心线程没有任务时最多保持多长时间后会销毁
- timeUnit:时间单位
- workQueue:阻塞队列
- threadFactory:创建线程的工厂
- handler:拒绝策略
-
JDK 5.0 起提供了线程池相关API:
Executorservice
和Executors
-
ExecutorService
:真正的线程池接口。常见子类ThreadPooExecutor
void execute(Runnable command)
:只能提交Runnable类型
的任务,无返回值<T> Future<T> submit( Callable<T>task)
:既能提交Runable类型
的任务,返回值为null,也能提交Callable类型
的任务,返回值为Futurevoid shutdown()
:关闭连接池void shutdownNow()
:立即关闭连接池
-
Executors
:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
1. -
Executor
类图
1.
4、线程池的五种状态
- Running
- 能接受新任务以及处理已添加的任务
- Shutdown
- 不接受新任务,可以处理已经添加的任务
- Stop
- 不接受新任务,不处理已经添加的任务,并且中断正在处理的任务
- Tidying
- 所有的任务已经终止,ct1记录的“任务数量”为0,ct1负责记录线程池的运行状态与活动线程数量
- Terminated
- 线程池彻底终止,则线程池转变为 terminated状态
1、CODE
public class ThreadPoolExecutor extends AbstractExecutorService {
/**
* 主池控制状态ctl是一个包含两个概念字段的原子整数
* workerCount:指示有效线程数
* runState:指示是否正在运行、关闭等
*
* 为了将它们打包成一个整数,我们将 workerCount 限制为
* (2^29)-1(约5亿)个线程,而不是(2^31)-1(20亿)个其他可
* 表示的线程。如果将来出现问题,可以将变量更改为原子长,并调整下面
* 的 移位/掩码 常数。但是,在需要之前,使用int会使代码更快、更简单
*
* workerCount 是允许启动和不允许停止的工人数。该值可能暂时不同于
* 活动线程的实际数量,例如,当 ThreadFactory 在被请求时无法创建
* 线程时,以及当退出线程在终止之前仍在执行簿记时。用户可见池大小报
* 告为工作集的当前大小
*
* 运行状态提供主要的生命周期控制,具有以下值:
* RUNNING: 接受新任务并处理排队的任务
* SHUTDOWN: 不接受新任务,但处理排队的任务
* STOP: 不接受新任务,不处理排队的任务,
* 以及中断正在进行的任务
* TIDYING: 所有任务都已终止, workerCount 为零,
* 转换到状态“TIDYING”的线程将运行
* terminated()钩子方法
* TERMINATED: terminated() 已经完成
*
* 这些值之间的数字顺序很重要,以便进行有序比较。运行状态随时间单调增加,
* 但不需要达到每个状态。过渡是:
* RUNNING -> SHUTDOWN
* 在调用 shutdown() 时,可能隐式地在 finalize() 中
* (RUNNING or SHUTDOWN) -> STOP
* 在调用 shutdownNow() 时
* SHUTDOWN -> TIDYING
* 当 queue 和 pool 都为空时
* STOP -> TIDYING
* 当 pool 为空时
* TIDYING -> TERMINATED
* 当 terminated() 钩子方法完成时
*
* 等待 awaitTermination() 的线程将在状态达到 TERMINATED 时返回
*
* 检测从 SHUTDOWN 到 TIDYING 的转换并不像您希望的那样简单,因为队列
* 在非空状态后可能会变为空,而在 SHUTDOWN 状态下也可能变为空。但我们
* 只能在看到队列为空后,看到 workerCount 为 0 时终止(这有时需要重新
* 检查——见下文)
*/
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 常量 29
private static final int COUNT_BITS = Integer.SIZE - 3;
// 低29位 存储 当前工作线程数
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 高3位 存储 运行状态
/**
* -1 = 1111 1111 1111 1111 1111 1111 1111 1111
* -1 << 29 = 1110 0000 0000 0000 0000 0000 0000 0000
* 高3位 = 111 ==> RUNNING
*/
private static final int RUNNING = -1 << COUNT_BITS;
/**
* 0 = 0000 0000 0000 0000 0000 0000 0000 0000
* 0 << 29 = 0000 0000 0000 0000 0000 0000 0000 0000
* 高3位 = 000 ==> SHUTDOWN
*/
private static final int SHUTDOWN = 0 << COUNT_BITS;
/**
* 1 = 0000 0000 0000 0000 0000 0000 0000 0001
* 1 << 29 = 0010 0000 0000 0000 0000 0000 0000 0000
* 高3位 = 001 ==> SHUTDOWN
*/
private static final int STOP = 1 << COUNT_BITS;
/**
* 2 = 0000 0000 0000 0000 0000 0000 0000 0010
* 2 << 29 = 0100 0000 0000 0000 0000 0000 0000 0000
* 高3位 = 010 ==> TIDYING
*/
private static final int TIDYING = 2 << COUNT_BITS;
/**
* 3 = 0000 0000 0000 0000 0000 0000 0000 0011
* 3 << 29 = 0110 0000 0000 0000 0000 0000 0000 0000
* 高3位 = 011 ==> TERMINATED
*/
private static final int TERMINATED = 3 << COUNT_BITS;
}
2、状态转换
5、execute
1、CODE
public class ThreadPoolExecutor extends AbstractExecutorService {
// ...
/**
* 在将来的某个时间执行给定的任务。该任务可以在新线程或现有池线程中执行。
*
* 如果由于此执行器已关闭或已达到其容量而无法提交任务以供执行,
* 则该任务将由当前 {RejectedExecutionHandler} 处理。
*
* 参数:command 要执行的任务
* 抛出:
* RejectedExecutionException 如果无法接受任务执行,则由{RejectedExecutionHandler}决定
* NullPointerException 如果命令为空
*/
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* 分3步进行:
*
* 1. 如果运行的线程少于 corePoolSize,请尝试使用给定命令作为其第一个任务启动新线程。
* 对addWorker的调用以原子方式检查运行状态和workerCount,因此通过返回false来
* 防止在不应该添加线程时添加线程的错误警报。
*
* 2. 如果一个任务可以成功地排队,那么我们仍然需要仔细检查是否应该添加一个线程(因为
* 存在自上次检查以来已有的线程已死亡),或者自进入此方法后池是否已关闭。因此,我
* 们重新检查状态,如果停止,则在必要时回滚排队,如果没有,则启动新线程。
*
* 3. 若我们无法将任务排队,那个么我们将尝试添加一个新线程。如果失败,我们知道我们已关
* 闭或饱和,因此拒绝该任务。
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
/*
* 创建、运行和清理的方法 在workers之后
*/
/**
* 检查是否可以根据当前池状态和给定绑定(核心或最大值)添加新工作线程。
* 如果是,将相应地调整工作人员计数,如果可能,将创建并启动一个新的
* 工作人员,并将firstTask作为其第一个任务运行。如果池已停止或符合
* 关闭条件,则此方法返回false。如果线程工厂在被请求时未能创建线程,
* 它也会返回false。如果线程创建失败,无论是由于线程工厂返回null,
* 还是由于异常(通常是 thread.start() 时的 OutOfMemoryError),
* 我们都会完全回滚.
*
* 入参:firstTask 新线程应首先运行的任务(如果没有,则为空)。
* 工作线程是用初始的第一个任务(在方法execute()中)
* 创建的,以在少于corePoolSize线程(在这种情况下,
* 我们总是启动一个)或队列已满(在这种情形下,我们必
* 须绕过队列)时绕过队列。最初,空闲线程通常是通过
* prestartCoreThread创建的,或者用来替换其他正
* 在死亡的工作线程.
*
* 入参:core 如果为true,则使用corePoolSize作为绑定,否则使用
maximumPoolSize。(此处使用布尔指示符,而不是值,
* 以确保在检查其他池状态后读取新值。)
*
* 返回:true 如果成功
*/
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 仅在必要时检查队列是否为空.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// 否则,由于workerCount更改,CAS失败;重试内部循环
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask); // 看下面的 class Worker,非常秀!
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 持锁时重新检查.
// 在ThreadFactory故障或在获取锁之前关闭时退出.
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // 预先检查 t 是否可启动
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start(); // 开启这个线程!!!!
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
/**
* Class Worker 主要维护运行任务的线程的中断控制状态,以及其他次要的簿记。
* 此类机会性地扩展了AbstractQueuedSynchronizer,以简化获取和释放围绕每
* 个任务执行的锁。这可以防止旨在唤醒等待任务的工作线程而不是中断正在运行的
* 任务的中断。我们实现了一个简单的非可重入互斥锁,而不是使用重入锁,因为我们
* 不希望工作任务在调用setCorePoolSize等池控制方法时能够重新获取锁。此外,
* 为了抑制中断直到线程实际开始运行任务,我们将锁状态初始化为负值,并在启动时
* 将其清除(在runWorker中).
*/
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
// ...
/** 此工作线程正在运行。如果工厂失败,则为 null. */
final Thread thread;
/** 要运行的初始任务。可能为 null. */
Runnable firstTask;
/**
* 使用来自ThreadFactory的给定第一个任务和线程创建.
* 入参:firstTask 第一个任务(如果没有,则为null)
*/
Worker(Runnable firstTask) {
setState(-1); // 在 runWorker 之前禁止中断
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** 将主运行循环委托给外部的 runWorker */
public void run() {
runWorker(this); // 往下看,把this传给外部的 runWorker 有多惊艳!
}
// ...
}
/**
* 主工作程序运行循环。重复从队列中获取任务并执行它们,同时处理许多问题:
*
* 1. 我们可以从初始任务开始,在这种情况下,我们不需要获得第一个任务。
* 否则,只要池在运行,我们就通过 getTask 获取任务。如果返回null,
* 则由于池状态或配置参数的改变,工作进程退出。其他退出是由外部代码中
* 的异常抛出导致的,在这种情况下,completedAbruptly 保持不变,
* 这通常会导致 processWorkerExit 替换此线程.
*
* 2. 在运行任何任务之前,获取锁以防止任务执行时其他池中断,然后我们
* 确保除非池停止,否则该线程不会设置其中断.
*
* 3. 每个任务运行之前都会调用 beforeExecute,这可能会引发异常,
* 在这种情况下,我们会导致线程在不处理任务的情况下死亡
* (completedAbruptly=true 时中断循环).
*
* 4. 假设 beforeExecute 正常完成,我们运行任务,收集任何抛出的
* 异常以发送给 afterExecute。我们分别处理 RuntimeException、
* Error(这两个都是规范保证我们捕获的)和任意 Throwables。因为
* 我们不能在 Runnable.run() 时重新抛出 Throwables,我们将它们
* 包装在输出的 Errors 中(到线程的 UncaughtExceptionHandler)
* 任何抛出的异常也会保守地导致线程死亡.
*
* 5. 任务完成后。运行完成后,我们调用 afterExecute,这也可能引发
* 异常,这也会导致线程死亡。根据JLS第14.20节的规定,即使是
* task.run() 抛出的.
*
* 异常机制的净效果是,afterExecute 和线程的
* UncaughtExceptionHandler 具有尽可能准确的信息,可以提供用户代码
* 遇到的任何问题.
*
* 入参:w 工人
*/
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // 允许中断
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
// 如果池正在停止,请确保线程已中断;
// 如果没有,请确保线程未中断。
// 这需要在第二种情况下进行重新检查,
// 以在清除中断时处理 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(); // 线程复用!!!!
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
}
2、分析一波
3、流程
6、CODE(暂时仅测试3个)
package mii.thread.demo17线程池;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 线程池
*/
public class 线程池 {
public static void main(String[] args) {
// 1、创建服务,创建线程池
ExecutorService pool = Executors.newCachedThreadPool(); // 快
//pool = Executors.newFixedThreadPool(10); // 慢
//pool = Executors.newSingleThreadExecutor(); // 非常慢
for (int i = 0; i < 1000; i++) {
// 2、将线程丢进池子执行
pool.execute(new MiThread(i));
}
// 3、关闭线程池
pool.shutdown();
}
}
class MiThread implements Runnable{
private int i;
public MiThread(int i){
this.i = i;
}
@Override
public void run() {
try {
Thread.sleep(1000); // 模拟业务耗时
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
7、Result+分析
1、newCachedThreadPool 可缓存线程池
1、Result
2、分析
1、newCachedThreadPool
/**
* 创建一个线程池,根据需要创建新线程,但在以前构建的线程可用时将重用它们。
* 这些池通常会提高执行许多短期异步任务的程序的性能。
* 调用execute将重用以前构造的线程(如果可用)。
* 如果没有可用的现有线程,将创建一个新线程并将其添加到池中。
* 60秒内未使用的线程将终止并从缓存中删除。
* 因此,空闲足够长时间的池不会消耗任何资源。
* 请注意,可以使用ThreadPoolExecutor构造函数创建具有类似属性但具有不同细节
* {例如 ThreadPoolExecutor}的池。
*
* 返回:新创建的线程池
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(
0, // corePoolSize:核心线程数
Integer.MAX_VALUE, // maximumPoolSize:最大线程数
60L, // keepAliveTime:存活时间
TimeUnit.SECONDS, // timeUnit:时间单位
new SynchronousQueue<Runnable>()); // workQueue:阻塞队列
}
2、图解
这里会涉及到
线程复用
跑了1000个任务,我这台机器跑的时候最多才创建了400多个线程,是因为非核心线程有一个 60s 的存活期限,虽然你手里活干完了,开始计时 60s,只要计时没结束来新活了你就接着干!妥妥的压榨啊!
2、newFixedThreadPool 固定个数线程池
1、Result
2、分析
1、newFixedThreadPool
/**
* 创建一个线程池,该线程池可重用在共享无边界队列上运行的固定数量的线程。
* 在任何时候,最多第n个线程将是活动的处理任务。
* 如果在所有线程都处于活动状态时提交了其他任务,则它们将在队列中等待,直到有一个线程可用。
* 如果任何线程在关机前的执行过程中由于故障而终止,则如果需要执行后续任务,将替换新线程。
* 池中的线程将一直存在,直到显式关闭为止。
*
* 参数:nThreads 池中的线程数
* 返回:新创建的线程池
* 抛出:IllegalArgumentException 如果nThreads<=0
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(
nThreads, // corePoolSize:核心线程数
nThreads, // maximumPoolSize:最大线程数
0L, // keepAliveTime:存活时间
TimeUnit.MILLISECONDS, // timeUnit:时间单位
new LinkedBlockingQueue<Runnable>()); // workQueue:阻塞队列
}
2、图解
这就是为什么控制台是10个10个的输出了
3、newSingleThreadExecutor 单线程池
1、Result
2、分析
1、newSingleThreadExecutor
/**
* 创建一个执行器,该执行器使用单个工作线程在无界队列上运行。
* (但是,请注意,如果此单个线程在关机前的执行过程中由于故障而终止,
* 则在需要执行后续任务时,新的线程将取代它。)
* 任务保证按顺序执行,并且在任何给定时间都不会有多个任务处于活动状态。
* 与其他等效的 newFixedThreadPool(1) 不同,
* 返回的执行器保证不可重新配置以使用其他线程。
*
* 返回:新创建的单线程执行器
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService(
new ThreadPoolExecutor(
1, // corePoolSize:核心线程数
1, // maximumPoolSize:最大线程数
0L, // keepAliveTime:存活时间
TimeUnit.MILLISECONDS, // timeUnit:时间单位
new LinkedBlockingQueue<Runnable>())); // workQueue:阻塞队列
}
2、图解
这就是为什么控制台打印的线程名永远是
thread-1
了
6、对比
- newCachedThreadPool
- 基本不会造成内存溢出
- 只会在创建巨量非核心线程时占满 CPU 更容易导致项目卡死(最大创建21.4亿个,CPU表示:非常淦!)
- newFixedThreadPool
- 如果队列里排队的任务过多,超过内存大小,就会造成内存溢出
- newSingleThreadExecutor
- 同newFixedThreadPool,更容易造成内存溢出
- 由于给定的线程池可操作性约等于0 ,所以一般都是用自己创建线程池,方便调优等
8、自定义线程池
1、CODE
package mii.thread.demo17线程池;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 自定义线程池
*/
public class 自定义线程池 {
public static void main(String[] args) {
ThreadPoolExecutor myPool = new ThreadPoolExecutor(
10, // corePoolSize:核心线程数
20, // maximumPoolSize:最大线程数
0L, // keepAliveTime:存活时间
TimeUnit.MILLISECONDS, // timeUnit:时间单位
new ArrayBlockingQueue<Runnable>(10) // workQueue:阻塞队列
);
for (int i = 0; i < 1000; i++) {
// 2、将线程丢进池子执行
myPool.execute(new MiThread(i));
}
// 3、关闭线程池
myPool.shutdown();
}
}
class MiniThread implements Runnable{
private int i;
public MiniThread(int i){
this.i = i;
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
2、Result
3、分析一波
默认的拒绝策略是 AbortPolicy
,
核心线程能分摊10个任务,队列里扔10个,非核心线程可分摊10个,等第31个任务进来的时候,没有空位了,直接拒绝!
< 小拓展 >
提交优先级:
- 核心线程优先级最高
- 队列次之
- 非核心线程最低
执行优先级:
- 核心线程优先级最高
- 非核心线程次之
- 队列最低
9、拒绝策略 RejectedExecutionHandler
1、类图
4个拒绝策略实现都是定义在
ThreadPoolExecutor
里的内部类
2、CODE
public class ThreadPoolExecutor extends AbstractExecutorService {
/**
* 默认的拒绝执行处理程序
*/
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();
/* 预定义的 RejectedExecutionHandler */
/**
* 被拒绝任务的处理程序,直接在{@code execute}方法的调用线程中运行被拒绝的
* 任务,除非执行器已关闭,在这种情况下,任务将被丢弃。
*/
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() { }
/**
* 在调用者的线程中执行任务r,除非执行器已关闭,在这种情况下,任务将被丢弃。
*
* 入参:r 请求执行的可运行任务
* 入参:e 试图执行此任务的执行者
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
/**
* 被拒绝任务的处理程序会抛出 {@code RejectedExecutionException} 异常
*/
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an {@code AbortPolicy}.
*/
public AbortPolicy() { }
/**
* 总是抛出 RejectedExecutionException.
*
* 入参:r 请求执行的可运行任务
* 入参:e 试图执行此任务的执行器
* 抛出:RejectedExecutionException 总是抛出
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
/**
* 被拒绝任务的处理程序,该处理程序静默地丢弃被拒绝的任务.
*/
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }
/**
* 不执行任何操作,这会导致丢弃任务r.
*
* 入参:r 请求执行的可运行任务
* 入参:e 试图执行此任务的执行者
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
/**
* 被拒绝任务的处理程序,丢弃最早的未处理请求,然后重试{@code execute},
* 除非执行器关闭,在这种情况下,任务被丢弃.
*/
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() { }
/**
* 获取并忽略执行器将执行的下一个任务(如果一个任务立即可用),然后重试执行任务r,
* 除非执行器关闭,在这种情况下,任务r将被丢弃。
*
* 入参:r 请求执行的可运行任务
* 入参:e 试图执行此任务的执行者
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
}
我们也可以自行拓展,自己实现
RejectedExecutionHandler
接口实现自定义的rejectedExecution
方法