线程池的 RejectedExecutionHandler(拒绝策略)

转载自  https://blog.csdn.net/jgteng/article/details/54411423

https://blog.csdn.net/luofenghan/article/details/78596950

JAVA为多线程场景提供了线程池,下面是一个线程池的构造方法:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }

其中这些参数的使用和说明在我的一篇文章中已经有了介绍,如果不太清楚的可以参考这篇文章:http://blog.csdn.net/jgteng/article/details/54409887
这里想对拒绝策略RejectedExecutionHandler做一下详细的介绍。

在使用线程池并且使用有界队列的时候,如果队列满了,任务添加到线程池的时候就会有问题,针对这些问题java线程池提供了以下几种策略:

  • AbortPolicy (中止) 
  • DiscardPolicy (抛弃) 
  • DiscardOldestPolicy  (抛弃最旧) 
  • CallerRunsPolicy (调用者运行)

自定义

AbortPolicy
该策略是线程池的默认策略。使用该策略时,如果线程池队列满了丢掉这个任务并且抛出RejectedExecutionException异常。

  • 该策略是默认的饱和策略;
  • 会抛出未检查的RejectedExecutionException,调用者可以捕获这个异常,然后根据需求编写自己的处理代码;

源码如下:

 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            //不做任何处理,直接抛出异常
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }

DiscardPolicy
这个策略和AbortPolicy的slient版本,如果线程池队列满了,会直接丢掉这个任务并且不会有任何异常。

  • 当提交的任务无法保存到队列中等待执行时,Discard策略会悄悄抛弃该任务。

源码如下:

   public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            //就是一个空的方法
        }

DiscardOldestPolicy

  • 会抛弃下一个将被执行的任务,然后尝试重新提交的新任务。
  • 如果工作队列是一个优先队列,那么抛弃最旧的策略,会抛弃优先级最高的任务,因此最好不要将抛弃最旧的饱和策略和优先级队列放在一起使用。


源码如下:

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                //移除队头元素
                e.getQueue().poll();
                //再尝试入队
                e.execute(r);
            }
        }

CallerRunsPolicy

  • 该策略既不会抛弃任务,也不会抛出异常,而是当线程池中的所有线程都被占用后,并且工作队列被填满后,下一个任务会在调用execute时在主线程中执行,从而降低新任务的流量。由于执行任务需要一定的时间,因此主线程至少在一定的时间内不能提交任何任务,从而使得工作者线程有时间来处理正在执行的任务。
  • 另一方面,在这期间,主线程不会调用accept,那么到达的请求将被保存在TCP层的队列中而不是在应用程序的队列中。如果持续过载,那么TCP层将最终发现他的请求队列被填满,因此同样会开始抛弃请求。
  • 当服务器过载时,这种过载情况会逐渐向外蔓延开来——从线程池到工作队列到应用程序再到TCP层,最终到达客户端,导致服务器在高负载的情况下实现一种平缓的性能降低。


源码如下:
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                //直接执行run方法
                r.run();
            }
        }

自定义
如果以上策略都不符合业务场景,那么可以自己定义一个拒绝策略,只要实现RejectedExecutionHandler接口,并且实现rejectedExecution方法就可以了。具体的逻辑就在rejectedExecution方法里去定义就OK了。
例如:我定义了我的一个拒绝策略,叫做MyRejectPolicy,里面的逻辑就是打印处理被拒绝的任务内容

public class MyRejectPolicy implements RejectedExecutionHandler{
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        //Sender是我的Runnable类,里面有message字段
        if (r instanceof Sender) {
            Sender sender = (Sender) r;
            //直接打印
            System.out.println(sender.getMessage());
        }
    }
}

这几种策略没有好坏之分,只是适用不同场景,具体哪种合适根据具体场景和业务需要选择,如果需要特殊处理就自己定义好了。

发布了76 篇原创文章 · 获赞 94 · 访问量 35万+

猜你喜欢

转载自blog.csdn.net/u012240455/article/details/89488317