手机版 欢迎访问it开发者社区(www.mfbz.cn)网站

当前位置: > 开发

线程池——拒绝策略——RejectedExecutionHandler——超负载解决办法

时间:2021/4/13 1:30:50|来源:|点击: 次

文章目录

  • 1:拒绝策略——超负载解决办法
    • 1.1 AbortPolicy策略演示
    • 1.2 CallerRunsPolicy 策略
    • 1.3 DiscardOldestPolicy 策略和DiscardPolicy策略
    • 1.4 自定义拒绝策略

1:拒绝策略——超负载解决办法

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

ThreadPoolExecutor类的最后一个参数指定了拒绝策略(RejectedExecutionHandler ), 也就是当任务数量超过系统实际承载能力时,就要用到拒绝策略了。拒绝策略可以说是系统超负荷运行时的补救措施,通常由于压力太大而引起的,也就是线程池中的线程已经用完了,无法继续为新任务服务,同时,等待队列中也已经排满了,再也放不下新任务了。这时,我们就需要有一套机制合理地处理这个问题。
JDK内置的四种拒绝策略,如下图
在这里插入图片描述

JDK内置的拒绝策略如下。

  • AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作。

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

  • DiscardOldestPolicy 策略:该策略将丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务。

  • DiscardPolicy策略:该策略默默地丢弃无法处理的任务,不予任何处理。如果允许任务丢失,这可能是最好的一种方案了吧。

ThreadPoolExecutor中默认的就是AbortPolicy策略
看ThreadPoolExecutor源码可知

    /**
     * The default rejected execution handler
     */
    private static final RejectedExecutionHandler defaultHandler =
        new AbortPolicy();

以上内置的策略均实现了RejectedExecutionHandler接口,若以上策略仍无法满足实际应用的需要,完全可以自己扩展RejectedExecutionHandler接口。RejectedExecutionHandler的定义如下:

public interface RejectedExecutionHandler{
void rejectedExecution (Runnable r, ThreadPoolExecutor executor);
//其中r为请求执行的任务,executor 为当前的线程池。
}

1.1 AbortPolicy策略演示

AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作。
看如下代码演示,

public class MyThreadDemo {
    public static void main(String[] args) {
        ExecutorService threadPoolExecutor = new ThreadPoolExecutor(2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<Runnable>(3),
   9            Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
//线程池中最大只可以容纳maximumPoolSize+workQueue可容纳数,本题就是5+3=8
12        for (int i=0;i<9;i++){
            threadPoolExecutor.execute(()-> {
                System.out.println(Thread.currentThread().getName() + "==正在处理业务");
            });
        }
    }
Exception in thread "main" java.util.concurrent.RejectedExecutionException:

由于该线程池只满足maximumPoolSize+workQueue(可容纳数),如果继续有线程进入,就会抛出异常。如代码12行有九个线程使用线程池,可是该线程池最大只可以容纳5+3=8个线程,又因为我们使用ThreadPoolExecutor.AbortPolicy拒绝策略所以,会抛出一个异常
代码第12行如果改成i<8则不会抛出异常。

1.2 CallerRunsPolicy 策略

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

public class MyThreadDemo {
    public static void main(String[] args) {
        ExecutorService threadPoolExecutor = new ThreadPoolExecutor(2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<Runnable>(3),
                Executors.defaultThreadFactory(),
9                new ThreadPoolExecutor.CallerRunsPolicy());
//线程池中最大只可以容纳maximumPoolSize+workQueue可容纳数,本题就是5+3=8
        for (int i=0;i<9;i++){
            threadPoolExecutor.execute(()-> {
                System.out.println(Thread.currentThread().getName() + "==正在处理业务");
            });
        }
    }
pool-1-thread-1==正在处理业务
main==正在处理业务
pool-1-thread-3==正在处理业务
pool-1-thread-2==正在处理业务
pool-1-thread-4==正在处理业务
pool-1-thread-3==正在处理业务
pool-1-thread-1==正在处理业务
pool-1-thread-5==正在处理业务
pool-1-thread-2==正在处理业务

上面代码测试中出现main==正在处理业务说明了,CallerRunsPolicy 策略直接在调用者线程(main)中,运行当前被丢弃的任务。

1.3 DiscardOldestPolicy 策略和DiscardPolicy策略

  • DiscardOldestPolicy 策略:该策略将丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务。

  • DiscardPolicy策略:该策略默默地丢弃无法处理的任务,不予任何处理。如果允许任务丢失,这可能是最好的一种方案了吧。

public class MyThreadDemo {
    public static void main(String[] args) {
        ExecutorService threadPoolExecutor = new ThreadPoolExecutor(2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<Runnable>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy());
//线程池中最大只可以容纳maximumPoolSize+workQueue可容纳数,本题就是5+3=8
        for (int i=0;i<20;i++){
            threadPoolExecutor.execute(()-> {
                System.out.println(Thread.currentThread().getName() + "==正在处理业务");
            });
        }
    }
pool-1-thread-2==正在处理业务
pool-1-thread-4==正在处理业务
pool-1-thread-2==正在处理业务
pool-1-thread-3==正在处理业务
pool-1-thread-1==正在处理业务
pool-1-thread-2==正在处理业务
pool-1-thread-4==正在处理业务
pool-1-thread-5==正在处理业务

代码测试说明,即使有20个线程进来,可是线程池最大只可以处理8个,其他的就全部丢弃不管了

1.4 自定义拒绝策略

由第4章可知,以上的四种内置的策略均实现了RejectedExecutionHandler接口,若以上策略仍无法满足实际应用的需要,完全可以自己扩展RejectedExecutionHandler接口
RejectedExecutionHandler的定义如下:

public interface RejectedExecutionHandler{
void rejectedExecution (Runnable r, ThreadPoolExecutor executor);
//其中r为请求执行的任务,executor 为当前的线程池。
}

我们观察一下ThreadPoolExecutor中的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());
        }
    }

该类就是ThreadPoolExecutor实现AbortPolicy策略的源码,我们可以根据它的源码自己也写出一个自定义AbortPolicy策略
如下代码演示

public class MyThreadDemo {
    public static void main(String[] args) {
        ExecutorService threadPoolExecutor = new ThreadPoolExecutor(2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<Runnable>(3),
                Executors.defaultThreadFactory(),
                new RejectedExecutionHandler() {
                    @Override
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                        System.out.println("我能怎么办我也很绝望啊!!!!");
                        throw new RejectedExecutionException("Task " + r.toString() +
                                " rejected from " +
                                executor.toString());
                    }
                });
//线程池中最大只可以容纳maximumPoolSize+workQueue可容纳数,本题就是5+3=8
        for (int i=0;i<20;i++){
            threadPoolExecutor.execute(()-> {
                System.out.println(Thread.currentThread().getName() + "==正在处理业务");
            });
        }
    }
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.zlj.juc.jucdemo.MyThreadDemo$$Lambda$1/1023892928@4dd8dc3 rejected from java.util.concurrent.ThreadPoolExecutor@6d03e736[Running, pool size = 5, active threads = 4, queued tasks = 0, completed tasks = 4]
pool-1-thread-1==正在处理业务
	at com.zlj.juc.jucdemo.MyThreadDemo$1.rejectedExecution(MyThreadDemo.java:34)
我能怎么办我也很绝望啊!!!!
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
pool-1-thread-2==正在处理业务
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
pool-1-thread-2==正在处理业务
	at com.zlj.juc.jucdemo.MyThreadDemo.main(MyThreadDemo.java:40)
pool-1-thread-2==正在处理业务
pool-1-thread-1==正在处理业务
pool-1-thread-3==正在处理业务
pool-1-thread-4==正在处理业务
pool-1-thread-5==正在处理业务

Copyright © 2002-2019 某某自媒体运营 版权所有