前言
在上文中我们对ThreadPoolExecutor进行了源码分析,当线程池中核心与非核心线程数,阻塞队列达到指定最大值时,之后再有任务提交会触发线程池的拒绝策略,本文我们来简单了解一下线程池的拒绝策略到底做了什么?
源码展示
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); //当添加核心线程失败,则会执行此方法
//内部就这么一行
// handler.rejectedExecution(command, this);表示执行拒绝策略
至于前面代码的逻辑,在线程池详细分析ThreadPoolExecutor一文中作了介绍.
那么handler.rejectedExecution(command, this)
具体是怎么执行拒绝策略的呢?
public interface RejectedExecutionHandler {
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
我们会发现该接口只有一个方法,根据官方文档,此接口有四个实现类
四个实现类都是ThreadPoolExecutor中的内部类,我们来具体看一下这四个拒绝策略的具体含义和用法
ThreadPoolExecutor.AbortPolicy
A handler for rejected tasks that throws a RejectedExecutionException.
拒绝任务,抛出一个RejectedExecutionException.
在线程池中默认使用的就是AbortPolicy,进入ThreadPoolExecutor.AbortPolicy中
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString()
+ " rejected from "
+ e.toString());
}
还记得上面我们说过–当线程池非核心任务添加失败会调用reject(command)方法吗?内部就是调用了这个方法,我们发现它的逻辑很简单,当有任务来之后,直接丢任务,并抛出RejectedExecutionException
异常,这是线程池默认执行的策略
ThreadPoolExecutor.DiscardPolicy
A handler for rejected tasks that silently discards the rejected task.
默默的丢弃一个被拒绝的任务
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
该拒绝策略也是比较简单,直接什么都不做.当有任务进来时,直接将任务抛弃,但不会抛出异常,
ThreadPoolExecutor.DiscardOldestPolicy
A handler for rejected tasks that discards the oldest unhandled request and then retries execute, unless the executor is shut down, in which case the task is discarded.
被拒绝任务的处理程序,它丢弃最早的未处理请求,然后重试执行,除非执行程序关闭,在这种情况下任务将被丢弃
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) { //if线程池不处于关闭状态
e.getQueue().poll(); //删除阻塞队列中的第一个任务
e.execute(r); //调用线程池的execute()方法重新执行次任务
}
}
如果线程池没有关闭,则会重新执行此任务,否则将任务抛弃,在这个决绝策略中,调用了线程池的阻塞队列,poll()函数返回队列中头部元素,并将其删除,再次执行任务
ThreadPoolExecutor.CallerRunsPolicy
A handler for rejected tasks that runs the rejected task directly in the calling thread of the execute method, unless the executor has been shut down, in which case the task is discarded.被拒绝任务的处理程序,它直接在执行方法的调用线程中运行被拒绝的任务,除非执行程序已关闭,在这种情况下任务将被丢弃。
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
runnable.run()在线程池中,是通过线程池创建的Worker线程执行start()来执行runnable任务的,线程池的本质是创建线程来执行任务,而此策略描述的直接在执行方法的线程调用,你可以回想一下自己在使用线程执行任务是怎样的?
Runnable runnable = new Runnable() {
@Override
public void run() {
}
};
new Thread(runnable).start();
这是使用线程来执行任务,而CallerRunsPolicy处理是直接使用runnable对象调用的run()来执行任务,所以该策略并没有创建线程来执行任务,而是使用对象直接调用了任务的run(),结合DiscardOldestPolicy使用executor()执行来理解
最后还有一个自定义拒绝策略,通过前面的叙述,相信你对拒绝策略也有了一个逻辑上的认识了,读过他的源码以后发现并没有想象的那么神秘,我们完全可以根据自己的需求来自己实现RejectedExecutionHandler接口来实现自己的拒绝策略
new ThreadPoolExecutor(1,1,1
, TimeUnit.HOURS
,new ArrayBlockingQueue<>(5)
,new ThreadPoolExecutor.AbortPolicy());
那么自己也来试试每一个拒绝策略的使用把?