Java并发编程系列之四:自定义线程池拒绝策略

105 阅读2分钟

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {

}

}

2、DiscardOldestPolicy: 丢弃队列中最老的任务, 尝试再次提交当前任务;

public static class DiscardOldestPolicy implements RejectedExecutionHandler {

public DiscardOldestPolicy() { }

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {

if (!e.isShutdown()) {

e.getQueue().poll();

e.execute(r);

}

}

}

3、 AbortPolicy: 直接抛异常,阻止系统正常工作;

public static class AbortPolicy implements RejectedExecutionHandler {

public AbortPolicy() { }

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {

throw new RejectedExecutionException("Task " + r.toString() +

" rejected from " +

e.toString());

}

}

4、CallerRunsPolicy: 将任务分给调用线程来执行,运行当前被丢弃的任务,这样做不会真的丢弃任务,但是提交的线程性能有可能急剧下降

public static class CallerRunsPolicy implements RejectedExecutionHandler {

public CallerRunsPolicy() { }

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {

if (!e.isShutdown()) {

r.run();

}

}

}

我们可以看得出来,前三种策略都是会丢弃原有的任务。但是在某些业务场景下,我们不能够粗暴的丢弃任务。第四种拒绝策略,是通过启动线程池的线程来处理丢弃的任务,但是问题是即便是线程池空闲,它也不会执行丢弃的任务,而是等待调用线程池的主线程来执行任务,直到任务结束。

二、自带线程池拒绝策略介绍


在线程池的定义中我们可以看到拒绝策略有个统一的实现接口,如下:

public interface RejectedExecutionHandler {

void rejectedExecution(Runnable r, ThreadPoolExecutor executor);

}

我们可以根据自己的业务需求来定义符合自己业务场景的处理策略。我们可以看下一些主流框架是如何自定义自己的处理策略的。

1、Netty 中的线程池拒绝策略

private static final class NewThreadRunsPolicy implements RejectedExecutionHandler {

NewThreadRunsPolicy() {

super();

}

public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {

try {

final Thread t = new Thread(r, "Temporary task executor");

t.start();

} catch (Throwable e) {

throw new RejectedExecutionException(

"Failed to start a new thread", e);

}

}

}

从上面的源码可以看出,Netty的处理方式就是不丢弃任务,这个思想和CallerRunsPolicy优点类似。只是在Netty框架中的自定义拒绝策略中,是通过新建工作线程来完成被丢弃的任务的,但是我们看一看得出它在创建线程时,没有进行条件约束,只要资源允许就不断创建新的线程来进行处理。

2、Dubbo 中的线程池拒绝策略

public class AbortPolicyWithReport extends ThreadPoolExecutor.AbortPolicy {

protected static final Logger logger = LoggerFactory.getLogger(AbortPolicyWithReport.class);

private final String threadName;

private final URL url;

private static volatile long lastPrintTime = 0;

private static Semaphore guard = new Semaphore(1);

public AbortPolicyWithReport(String threadName, URL url) {

this.threadName = threadName;

this.url = url;

}

@Override

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {

String msg = String.format("Thread pool is EXHAUSTED!" +

" Thread Name: %s, Pool Size: %d (active: %d, core: %d, max: %d, largest: %d), Task: %d (completed: %d)," +

" Executor status:(isShutdown:%s, isTerminated:%s, isTerminating:%s), in %s://%s:%d!",

threadName, e.getPoolSize(), e.getActiveCount(), e.getCorePoolSize(), e.getMaximumPoolSize(), e.getLargestPoolSize(),