线程池中的拒绝策略

  • 一、拒绝策略产生缘由?
  • 二、何时触发使用拒绝策略
  • 三、拒绝策略都有哪些?
  • 1、AbortPolicy策略
  • 2、CallerRunsPolicy策略
  • 3、DiscardOldestPolicy策略
  • 4、DiscardPolicy策略
  • 5、自定义拒绝策略
  • 四、线程池使用拒绝策略demo


一、拒绝策略产生缘由?

线程池工作中,如果任务量很大,超过系统实际承载能力时,如果不予理睬,接着可能系统就崩溃了,所以jdk内置提供了线程池的4种拒绝策略,合理的解决这种问题。

二、何时触发使用拒绝策略

线程池中线程已经用完不能再创建了,等待队列也排满了,此时如果再来新任务,就会执行下面的拒绝策略之一。

主要配合线程池使用

三、拒绝策略都有哪些?

1、AbortPolicy策略

使用该策略会直接抛异常,阻止系统正常工作。

2、CallerRunsPolicy策略

只要线程池没有关闭,该策略直接在调用者线程中,运行当前被丢弃的任务,虽然不会真的丢弃任务,但是任务提交线程的性能极有可能急剧下降。

3、DiscardOldestPolicy策略

该策略将丢弃即将执行的下一个任务,并尝试再次提交当前任务。

4、DiscardPolicy策略

该策略默默丢弃没法处理的任务,不予处理。如果允许任务丢失,这可能是最好的一种解决方案了。

5、自定义拒绝策略

1)、先来看下 jdk内置拒绝策略的定义。

java 线程池满了拒绝策略 java线程池默认拒绝策略_自定义


DiscardOldestPolicy 的源码实现:

java 线程池满了拒绝策略 java线程池默认拒绝策略_自定义_02


作为子类,他们都实现了 RejectedExecutionHandler 接口 ,通过重写接口方法(参数 r 为任务线程,参数 e 为当前的线程池 ),以父类作为参数传入自定义的线程池 (如果觉得描述不清楚,可参看下面线程池通过接口回调的方式使用拒绝策略的小demo)。

java 线程池满了拒绝策略 java线程池默认拒绝策略_java 线程池满了拒绝策略_03


不知道小伙伴们看着眼熟不,乍一看,呦呵,这个自定义线程池还默默的用了一下设计模式呢~

拒绝策略以接口形式作为参数,实际调用的时候传入的是该接口的子类,而线程池使用的是其重写的拒绝策略方法。soga~原来是使用了策略模式啊。但是如果您看这个线程池的核心工作代码,又能知道了,他还用了模板模式,所以准确来讲,他使用了典型的组合:模板模式+策略模式

2)、哦哦,原来酱紫啊,如果jdk内置拒绝策略无法满足你的需求,那你也可以自定义拒绝策略咯~
就是照着葫芦画片,也实现这个接口,并重写他的方法呀。调用的话,就按照上面的截图形式,把子类传进去,或者接口回调的形式也可以。

到此,拒绝策略介绍完毕。

如果觉得上面阐述的不够清楚,可以看下面的demo

四、线程池使用拒绝策略demo

public class RejectThreadPoolDemo {
    public static void main(String[] args) throws InterruptedException {
        MyTask task=new MyTask();
      ExecutorService es=  new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(10), Executors.defaultThreadFactory(), new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                System.out.println(r.toString()+" is discard");
            }
        });

      for (int i=0;i<Integer.MAX_VALUE;i++){
          es.submit(task);
          Thread.sleep(10);
      }
    }
    public static class MyTask implements Runnable{
        @Override
        public void run(){
            System.out.println(System.currentTimeMillis()+": Thread ID :"+Thread.currentThread().getId());

            try {
                Thread.sleep(100);
            }catch (InterruptedException e){
                System.out.println(e);
            }
        }
    }
}

结果;

java 线程池满了拒绝策略 java线程池默认拒绝策略_线程池的拒绝策略_04


该线程池核心线程数和最大线程数都是5,等待队列容量为10个,自定义拒绝策略,不抛出异常,避免任务提交端没做异常处理导致出现其他的状况。选择只打印丢弃任务的信息,比内置的DiscardPolicy策略高级一点点。

在实际应用中,可以将日志信息记录的更详细一点,来分析系统的负载和任务丢失的情况。

这种固定大小的线程池,最好不要设置无界队列,如果任务量很大,消费又很慢,那队列会一直扩容,很可能把内存撑爆。