ThreadPoolExecutor的应用和实现分析(续)—— 任务饱和丢弃策略


在前面三篇文章中,我们已经对ThreadPoolExecutor的应用以及任务处理和生命周期相关的源码实现做了整理分析。这篇我们简要整理下java.util.concurrent包中的RejectedExecutionHandler这个接口和对应的实现类。

0. RejectedExecutionHandler接口

当ThreadPoolExecutor执行任务的时候,如果线程池的线程已经饱和,并且任务队列也已满。那么就会做丢弃处理,这也是execute()方法实现中的操作,源码如下:

1
2
else if (!addWorker(command, false ))
         reject(command);

这个reject()方法很简单,直接调用丢弃处理的handler方法的rejectedExecution()。

在java.util.concurrent中,专门为此定义了一个接口,是RejectedExecutionHandler

1
2
3
public interface RejectedExecutionHandler {
     void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}

其中只有rejectedExecution()一个方法。返回为void,而参数一个是具体的Runnable任务,另一个则是被提交任务的ThreadPoolExecutor。

凡是实现了这个方法的类都可以作为丢弃处理器在ThreadPoolExecutor对象构造的时候作为参数传入,这个前面的文章已经提到过了。其中ThreadPoolExecutor给出了4种基本策略的实现。分别是:

  • CallerRunsPolicy
  • AbortPolicy
  • DiscardPolicy
  • DiscardOldestPolicy

下面分别详细说明。

1. 直接丢弃

这个也是实现最简单的类,其中的rejectedExecution()方法是空实现,即什么也不做,那么提交的任务将会被丢弃,而不做任何处理。

1
2
3
4
5
6
public static class DiscardPolicy implements RejectedExecutionHandler {
     public DiscardPolicy() { }
 
     public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
     }
}

这个策略使用的时候要小心,要明确需求。不然不知不觉的任务就丢了。

2. 丢弃最老

和上面的有些类似,也是会丢弃掉一个任务,但是是队列中最早的。

实现如下:

1
2
3
4
5
6
7
8
9
10
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
     public DiscardOldestPolicy() { }
 
     public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
         if (!e.isShutdown()) {
             e.getQueue().poll();
             e.execute(r);
         }
     }
}

注意,会先判断ThreadPoolExecutor对象是否已经进入SHUTDOWN以后的状态。之后取出队列头的任务并不做任何处理,即丢弃,再重新调用execute()方法提交新任务。

3. 废弃终止

这个RejectedExecutionHandler类和直接丢弃不同的是,不是默默地处理,而是抛出java.util.concurrent.RejectedExecutionException异常,这个异常是RuntimeException的子类。这个策略实现如下:

1
2
3
4
5
6
7
8
9
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());
     }
}

注意,处理这个异常的线程是执行execute()的调用者线程。

4. 调用者执行策略

在这个策略实现中,任务还是会被执行,但线程池中不会开辟新线程,而是提交任务的线程来负责维护任务。

1
2
3
4
5
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
     if (!e.isShutdown()) {
         r.run();
     }
}

注意,和DiscardOldestPolicy同样,也会先判断ThreadPoolExecutor对象的状态,之后执行任务。这样处理的一个好处,是让caller线程运行任务,以推迟该线程进一步提交新任务,有效的缓解了线程池对象饱和的情况。

上面只是SunJDK中提供的4种最基本策略,开发者可以根据具体需求定制。

此外,前文提到ThreadPoolExecutor也可以进行扩展。在java.util.concurrent包中有ScheduledThreadPoolExecutor这样一个类,其扩展了ThreadPoolExecutor,实现了一些时间和任务调度相关的方法。这里我就不具体整理了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值