转: java多线程-ThreadPoolExecutor的拒绝策略RejectedExecutionHandler

转自:  https://blog.csdn.net/qq_25806863/article/details/71172823

 

概述

原文地址 http://blog.csdn.net/qq_25806863/article/details/71172823

在分析ThreadPoolExecutor的构造参数时,有一个RejectedExecutionHandler参数。

RejectedExecutionHandler是一个接口:

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

    1
    2
    3

里面只有一个方法。当要创建的线程数量大于线程池的最大线程数的时候,新的任务就会被拒绝,就会调用这个接口里的这个方法。

可以自己实现这个接口,实现对这些超出数量的任务的处理。

ThreadPoolExecutor自己已经提供了四个拒绝策略,分别是CallerRunsPolicy,AbortPolicy,DiscardPolicy,DiscardOldestPolicy

这四个拒绝策略其实一看实现方法就知道很简单。
AbortPolicy

ThreadPoolExecutor中默认的拒绝策略就是AbortPolicy。直接抛出异常。

private static final RejectedExecutionHandler defaultHandler =
    new AbortPolicy();

    1
    2

下面是他的实现:

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());
    }
}

    1
    2
    3
    4
    5
    6
    7
    8

很简单粗暴,直接抛出个RejectedExecutionException异常,也不执行这个任务了。
测试

先自定义一个Runnable,给每个线程起个名字,下面都用这个Runnable

static class MyThread implements Runnable {
        String name;
        public MyThread(String name) {
            this.name = name;
        }
        @Override
        public void run() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程:"+Thread.currentThread().getName() +" 执行:"+name +"  run");
        }
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

然后构造一个核心线程是1,最大线程数是2的线程池。拒绝策略是AbortPolicy

ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 0,
        TimeUnit.MICROSECONDS,
        new LinkedBlockingDeque<Runnable>(2),
        new ThreadPoolExecutor.AbortPolicy());

    1
    2
    3
    4

for (int i = 0; i < 6; i++) {
    System.out.println("添加第"+i+"个任务");
    executor.execute(new MyThread("线程"+i));
    Iterator iterator = executor.getQueue().iterator();
    while (iterator.hasNext()){
        MyThread thread = (MyThread) iterator.next();
        System.out.println("列表:"+thread.name);
    }
}

    1
    2
    3
    4
    5
    6
    7
    8
    9

输出是:

这里写图片描述

分析一下过程。

    添加第一个任务时,直接执行,任务列表为空。
    添加第二个任务时,因为采用的LinkedBlockingDeque,,并且核心线程正在执行任务,所以会将第二个任务放在队列中,队列中有 线程2.
    添加第三个任务时,也一样会放在队列中,队列中有 线程2,线程3.
    添加第四个任务时,因为核心任务还在运行,而且任务队列已经满了,所以胡直接创建新线程执行第四个任务,。这时线程池中一共就有两个线程在运行了,达到了最大线程数。任务队列中还是有线程2, 线程3.
    添加第五个任务时,再也没有地方能存放和执行这个任务了,就会被线程池拒绝添加,执行拒绝策略的rejectedExecution方法,这里就是执行AbortPolicy的rejectedExecution方法直接抛出异常。
    最终,只有四个线程能完成运行。后面的都被拒绝了。

CallerRunsPolicy

CallerRunsPolicy在任务被拒绝添加后,会调用当前线程池的所在的线程去执行被拒绝的任务。

下面说他的实现:

public static class CallerRunsPolicy implements RejectedExecutionHandler {
    public CallerRunsPolicy() { }
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            r.run();
        }
    }
}

    1
    2
    3
    4
    5
    6
    7
    8

也很简单,直接run。
测试

ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 30,
        TimeUnit.SECONDS,
        new LinkedBlockingDeque<Runnable>(2),
        new ThreadPoolExecutor.AbortPolicy());

    1
    2
    3
    4

按上面的运行,输出

这里写图片描述

注意在添加第五个任务,任务5 的时候,同样被线程池拒绝了,因此执行了CallerRunsPolicy的rejectedExecution方法,这个方法直接执行任务的run方法。因此可以看到任务5是在main线程中执行的。

从中也可以看出,因为第五个任务在主线程中运行,所以主线程就被阻塞了,以至于当第五个任务执行完,添加第六个任务时,前面两个任务已经执行完了,有了空闲线程,因此线程6又可以添加到线程池中执行了。

这个策略的缺点就是可能会阻塞主线程。
DiscardPolicy

这个策略的处理就更简单了,看一下实现就明白了:

public static class DiscardPolicy implements RejectedExecutionHandler {
    public DiscardPolicy() { }
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
}

    1
    2
    3
    4
    5

这个东西什么都没干。

因此采用这个拒绝策略,会让被线程池拒绝的任务直接抛弃,不会抛异常也不会执行。
测试

ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 30,
        TimeUnit.SECONDS,
        new LinkedBlockingDeque<Runnable>(2),
        new ThreadPoolExecutor.DiscardPolicy());

    1
    2
    3
    4

输出:

这里写图片描述

可以看到 后面添加的任务5和6根本不会执行,什么反应都没有,直接丢弃。
DiscardOldestPolicy

DiscardOldestPolicy策略的作用是,当任务呗拒绝添加时,会抛弃任务队列中最旧的任务也就是最先加入队列的,再把这个新任务添加进去。

public static class DiscardOldestPolicy implements RejectedExecutionHandler {
    public DiscardOldestPolicy() { }
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            e.getQueue().poll();
            e.execute(r);
        }
    }
}

    1
    2
    3
    4
    5
    6
    7
    8
    9

在rejectedExecution先从任务队列总弹出最先加入的任务,空出一个位置,然后再次执行execute方法把任务加入队列。
测试

ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 30,
        TimeUnit.SECONDS,
        new LinkedBlockingDeque<Runnable>(2),
        new ThreadPoolExecutor.DiscardOldestPolicy());

    1
    2
    3
    4

输出是:

这里写图片描述

可以看到,

    在添加第五个任务时,会被线程池拒绝。这时任务队列中有 任务2,任务3
    这时,拒绝策略会让任务队列中最先加入的任务弹出,也就是任务2.
    然后把被拒绝的任务5添加人任务队列,这时任务队列中就成了 任务3,任务5.
    添加第六个任务时会因为同样的过程,将队列中的任务3抛弃,把任务6加进去,任务队列中就成了 任务5,任务6
    因此,最终能被执行的任务只有1,4,5,6. 任务2和任务3倍抛弃了,不会执行。

自定义拒绝策略

通过看前面的系统提供的四种拒绝策略可以看出,拒绝策略的实现都非常简单。自己写亦一样

比如现在想让被拒绝的任务在一个新的线程中执行,可以这样写:

static class MyRejectedExecutionHandler implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        new Thread(r,"新线程"+new Random().nextInt(10)).start();
    }
}

    1
    2
    3
    4
    5
    6

然后正常使用:

ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 30,
        TimeUnit.SECONDS,
        new LinkedBlockingDeque<Runnable>(2),
        new MyRejectedExecutionHandler());

    1
    2
    3
    4

输出:

这里写图片描述

发现被拒绝的任务5和任务6都在新线程中执行了。
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/330109.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

已知两点坐标如何快速增加其他坐标_「测绘精选」坐标转换概述

引言&#xff1a;这篇“坐标转换概述”献给各位&#xff0c;可以对坐标转换有一个大致地、整体地了解。文中有些名词是为了便于表达而自创的&#xff0c;大家不用考据、较真。一、静态坐标和动态坐标(1)静态坐标传统大地测量没有考虑板块运动对坐标的影响。虽然板块运动客观存在…

什么是G1垃圾回收算法

转载自 什么是G1垃圾回收算法为解决CMS算法产生空间碎片和其它一系列的问题缺陷&#xff0c;HotSpot提供了另外一种垃圾回收策略&#xff0c;G1&#xff08;Garbage First&#xff09;算法&#xff0c;通过参数 -XX:UseG1GC来启用&#xff0c;该算法在JDK 7u4版本被正式推出&am…

一文理清RocketMQ顺序消费、重复消费、消息丢失问题

前言 在使用消息队列时不可避免的会遇到顺序消费、重复消费、消息丢失三个问题。在一次面试字节的时候&#xff0c;面试官问到如何保证顺序消费&#xff0c;当时回答不太准确&#xff0c;特意此文回顾如何解决顺序消费、重复消费、消息丢失三个问题。 重复消费 解决重复消费…

一道丧心病狂的java面试题

转载自 一道丧心病狂的java面试题无意中了解到如下题目&#xff0c;觉得蛮好。 题目如下&#xff1a; public class TestSync2 implements Runnable {int b 100; synchronized void m1() throws InterruptedException {b 1000;Thread.sleep(500); //6System.out.pri…

水晶报表图形位置_看了我用Excel做的年度报表,老板直夸好

2020年前5个月&#xff0c;最火爆的莫过于口罩。口罩的整条产业链都变得炙手可热&#xff0c;口罩、口罩机、炒熔喷布、聚丙烯等等相关企业的业务数据往往都是去年的几倍。那我们现在作为一家“表姐牌”的口罩厂的员工&#xff0c;老板叫我用Excel做一个既酷炫又简洁的年度报表…

Mysql优化(三):优化order by

MySQL中的两种排序方式 .通过有序索引顺序扫描直接返回有序数据 因为索引的结构是B树&#xff0c;索引中的数据是按照一定顺序进行排列的&#xff0c;所以在排序查询中如果能利用索引&#xff0c;就能避免额外的排序操作。EXPLAIN分析查询时&#xff0c;Extra显示为Using inde…

漫画:什么是服务熔断

转载自 漫画&#xff1a;什么是服务熔断什么是服务熔断&#xff1f;熔断这一概念来源于电子工程中的断路器&#xff08;Circuit Breaker&#xff09;。在互联网系统中&#xff0c;当下游服务因访问压力过大而响应变慢或失败&#xff0c;上游服务为了保护系统整体的可用性&#…

rabbitmq手动确认ack

【README】 参考 https://blog.csdn.net/u012943767/article/details/79300673 &#xff1b; 【0】声明交换机&#xff0c;队列 与绑定 /*** 交换机&#xff0c;队列声明与绑定 */ public class AckDeclarer {/** 确认交换机 */public static final String ACK_EXCHANGE2 &q…

python图片保存_python读取和保存图片5种方法对比

python读取和保存图片5种方法对比 python中对象之间的赋值是按引用传递的&#xff0c;如果需要拷贝对象&#xff0c;需要用到标准库中的copy模块 方法一&#xff1a;利用 PIL 中的 Image 函数 这个函数读取出来不是 array 格式&#xff0c;这时候需要用 np.asarray(im) 或者 np…

finally块不被执行的情况总结

finally块的作用 通常用于处理善后工作。当try块里出现异常时&#xff0c;会立即跳出try块&#xff0c;到catch块匹配对应的异常&#xff0c;执行catch块里的语句。此时&#xff0c;可能在try块里存在打开的文件没关闭&#xff0c;连接的网络没断开&#xff0c;这部分资源是GC…

rabbitmq生产者基于事务实现发送确认

【README】 业务场景&#xff1a; 业务处理伴随消息的发送&#xff0c;业务处理失败&#xff08;事务回滚&#xff09;后要求消息不发送。 补充1&#xff1a;ACK与CONFIRM的区别 ACK-消费者消费成功后确认&#xff1b;&#xff08;消费者确认已收到&#xff09; CONFIRM-事…

什么是CAP定理

转载自 什么是CAP定理计算机界有很多高大上又难于理解的术语&#xff0c;CAP就是其中之一&#xff0c; 什么一致性&#xff08;Consistency&#xff09;&#xff0c; 可用性&#xff08;Availability&#xff09;&#xff0c; 分区容错性&#xff08;Partition tolerance&#…

python找不到指定的文件夹里_Python环球网在Unix中的指定文件路径中找不到*.txt

我在Windows环境中写了一些文件&#xff0c;我在转换文件时遇到了麻烦。在Windows中&#xff0c;我通常使用类似以下内容读取目录中的所有.txt文件&#xff1a;pathtotxt "C:\\Text Data\\EJC\\Philosophical Transactions 1665-1678\\*\\*.txt" for file in glob.g…

从开发者角度谈Mysql主键

转载自 从开发者角度谈Mysql主键说在前面零度mysql一直比较薄弱&#xff0c;俗话说的好&#xff0c;不会mysql的程序员不是好程序员&#xff0c;刚刚好认识mysql大牛刘龘刘&#xff0c;刚刚好就有了这些文章&#xff0c;主要是刘龘刘大牛写的&#xff0c;零度稍微修改成文&…

JVM内存结构分析:为什么需要S0和S1?

一、为什么会有年轻代 我们先来屡屡&#xff0c;为什么需要把堆分代&#xff1f;不分代不能完成他所做的事情么&#xff1f;其实不分代完全可以&#xff0c;分代的唯一理由就是优化GC性能。你先想想&#xff0c;如果没有分代&#xff0c;那我们所有的对象都在一块&#xff0c;…

rabbitmq-消息追踪rabbitmq_tracing

【README】 消息中心的消息追踪需要使用 Trace 实现&#xff0c;Trace是 rabbitmq用于记录每一次发送的消息&#xff1b;方便开发者调试&#xff0c;排错。可通过插件形式提供可视化界面。 【1】 开启消息追踪 1&#xff09;消息追踪通过 rabbitmq的插件 trace 来实现&#x…

python大神交流网站_学习Python必去的8个网站

作为一个现时代的程序员初学者&#xff0c;除了看书之外&#xff0c;互联网的学习手段也是断不能少的&#xff01; 以下这些网站&#xff0c;虽说不上全方位的满足你的需求&#xff0c;但是大部分也都能&#xff01; 0.国外的大神GitHub &#xff1a; https://github.com/pypa/…

一文理清Http2.0

前言 HTTP2.0主要有三大特性&#xff1a;二进制协议、头部压缩、服务端推送&#xff08;Server Push&#xff09;、多路复用。本文从分析http1.x存在的问题入手&#xff0c;逐一介绍http2.0的优势。 Http1.x存在的问题 在HTTP1.0下&#xff0c;HTTP1.1非Keep-Alive模式下&am…

收敛和发散思维如何被用作解决问题的手段

转自&#xff1a; https://www.jdon.com/mda/thinking.html 将发散和收敛的思想结合在一起使用时&#xff0c;可以帮助分析人员得出更好的和更有创造性的解决方案。发散性思维是将一个话题分解并产生许多从原始概念中分支出来的想法的过程&#xff0c;而收敛性思维则是集中于少…

关于python面向对象编程中、下列说法中_关于Python面向对象编程的知识点总结

前言 如果你以前没有接触过面向对象的编程语言&#xff0c;那你可能需要先了解一些面向对象语言的一些基本特征&#xff0c;在头脑里头形成一个基本的面向对象的概念&#xff0c;这样有助于你更容易的学习Python的面向对象编程。 接下来我们就来了解关于Python面向对象编程的知…