注册

线程池系列分析-线程池的拒绝策略

前言


线程池系列的第二篇文章。拒绝策略的说明。技术人嘛。还是要经常归纳总结的


什么是拒绝策略


首先要明白,为什么线程池要有一个拒绝策略。也就是他出现的背景是什么。
了解过线程池的小伙伴应该都知道。线程池的构造参数中就有一个拒绝策略


public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
//拒绝策略的接口
RejectedExecutionHandler handler) {
复制代码

拒绝。意味着不满足某些条件,线程池也是一样。当线程数超过了maximumPoolSize的时候。就会拒绝添加任务。起到了保护线程池的作用


有哪些拒绝策略


RejectedExecutionHandler本身也是一个接口。如下


public interface RejectedExecutionHandler {
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
复制代码

线程池本身提供了4中不同的拒绝策略




图片来源Java线程池实现原理及其在美团业务中的实践


AbortPolicy



可以看到。非常简单粗暴,直接抛出一个异常。


DiscardPolicy



嘿,啥也不管。啥也不问。任务拒绝就拒接了。也不给个提示啥的。就相当于直接把任务丢弃


DiscardOldestPolicy



如果线程池还在运行。那么就将阻塞队列中最前面的任务给取消,在执行当前任务
这么说有点玄乎。笔者写了一个简单的测试代码。能够更加描述清楚。笔者仿造DiscardOldestPolicy写了一个一摸一样的拒绝策略。然后加上打印。观察



完整的测试代码点击查看

public class Main {

public static class CustomDiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public CustomDiscardOldestPolicy() {
}

/**
* 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()) {
System.out.println("被拒绝的任务 " + r.toString());
Runnable runnable = e.getQueue().poll();
if (runnable != null)
System.out.println("队列中拿到的任务 " + runnable.toString());
else
System.out.println("队列中拿到的任务 null");
e.execute(r);
}
}
}

public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(
1,
2,
60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(2),
new CustomDiscardOldestPolicy()
);

for (int i = 0; i < 5; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("任务执行---开始" + Thread.currentThread().getName() + " 任务 " + this.toString());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
System.out.println("添加任务 " + runnable.toString());
pool.execute(runnable);
}
}
}

复制代码


在上面的测试代码中。笔者将最大线程数控制在2个。核心线程数控制在1个。并且选择了一个有长度的队列ArrayBlockingQueue。设置其长度为2。



  • 当任务1添加进入以后。因为核心线程数是1.所以直接创建新的线程执行任务。
  • 当任务2添加进入以后。因为超过了核心线程数1.所以被添加到队列当中。此时的队列中有一个任务2
  • 当任务3添加进入以后。同理。被添加到队列当中。此时队列当中有两个任务了。
  • 当任务4添加进入以后。这个时候队列因为已经满了。所以判断是否超过了最大线程数。超过了就直接拒绝策略。

CallerRunsPolicy



默认的拒绝策略


在线程池的构造方法中可以看到 有一个defaultHandler拒绝策略


public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
复制代码

defaultHandler给我们创建了一个AbortPolicy。这也是线程池默认的策略。就是直接抛出异常


private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();

作者:在雨季等你
链接:https://juejin.cn/post/6955058450619432974
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

1 个评论

学习了

要回复文章请先登录注册