详解Java ThreadPoolExecutor的拒绝策略

目录
  • 背景
  • 线程池基本原理
  • 线程池拒绝策略
    • AbortPolicy
    • CallerRunsPolicy
    • DiscardPolicy
    • 示例
    • 执行结果
    • DiscardOldestPolicy
    • 示例说明
    • 自定义拒绝策略
  • 小结
  • 总结

背景

线程池的技术在项目中使用广泛,线程池提供了四种拒绝策略,大家是否了解这四种拒绝的策略呢?本文将详细的讲解ThreadPoolExecutor的四种拒绝策略,以及相关的注意事项。

线程池基本原理

线程池的原理如下图:

说明:

  • 当前运行的线程少于corePoolSize,则创建新线程来执行任务。
  • 运行的线程等于或多于corePoolSize,则将任务添加到队列中。
  • 当任务队列已满,则在非corePool中创建新的线程来处理任务。
  • 创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法。

线程池拒绝策略

线程池为我们提供了四种拒绝策略分别是:CallerRunsPolicy,AbortPolicy,DiscardPolicy,DiscardOldestPolicy

AbortPolicy

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

说明:这种策略非常简单粗暴,直接抛出RejectedExecutionException异常,也不会执行后续的任务。

示例说明:

public class ThreadPoolTest
{
    public static void main(String[] args)
    {
        ThreadPoolExecutor threadPoolExecutor  = new ThreadPoolExecutor(
                2,
                5,
                10,
                TimeUnit.MICROSECONDS,
                new LinkedBlockingDeque<>(1),
                new ThreadPoolExecutor.AbortPolicy());

        //异步执行
        for(int i=0; i<10;i++)
        {
          System.out.println("添加第"+i+"个任务");
          threadPoolExecutor.execute(new TestThread("线程"+i));
        }
    }
}

public class TestThread implements Runnable
{
    private String name;
    public TestThread(String name){
        this.name=name;
    }

    @Override
    public void run()
    {
        try
        {
            Thread.sleep(1000);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        System.out.println("thread name:"+Thread.currentThread().getName()+",执行:"+name);
    }
}

执行结果:

Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.skywares.fw.juc.thread.TestThread@55f96302 rejected from java.util.concurrent.ThreadPoolExecutor@3d4eac69[Running, pool size = 5, active threads = 5, queued tasks = 1, completed tasks = 0]
    at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
    at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
    at com.skywares.fw.juc.thread.ThreadPoolTest.main(ThreadPoolTest.java:26)
thread name:pool-1-thread-5,执行:线程5
thread name:pool-1-thread-2,执行:线程1
thread name:pool-1-thread-4,执行:线程4
thread name:pool-1-thread-3,执行:线程3
thread name:pool-1-thread-1,执行:线程0
thread name:pool-1-thread-5,执行:线程2

从执行结果我们得知,采用AbortPolicy策略当任务执行到第七个任务时会直接报错,导致后续的业务逻辑不会执行。

CallerRunsPolicy

CallerRunsPolicy在任务被拒绝添加后,会用调用execute函数的上层线程去执行被拒绝的任务。

相关示例

public class ThreadPoolTest
{
    public static void main(String[] args)
    {
        ThreadPoolExecutor threadPoolExecutor  = new ThreadPoolExecutor(
                2,
                5,
                10,
                TimeUnit.MICROSECONDS,
                new LinkedBlockingDeque<>(1),
                new ThreadPoolExecutor.CallerRunsPolicy());

        //异步执行
        for(int i=0; i<10;i++)
        {
          System.out.println("添加第"+i+"个任务");
          threadPoolExecutor.execute(new TestThread("线程"+i));
        }
    }
}

执行结果:

添加第0个任务
添加第1个任务
添加第2个任务
添加第3个任务
添加第4个任务
添加第5个任务
添加第6个任务
thread name:main,执行:线程6
thread name:pool-1-thread-3,执行:线程3
thread name:pool-1-thread-1,执行:线程0
thread name:pool-1-thread-4,执行:线程4
thread name:pool-1-thread-2,执行:线程1
thread name:pool-1-thread-5,执行:线程5
添加第7个任务
添加第8个任务
thread name:main,执行:线程8
thread name:pool-1-thread-1,执行:线程7
thread name:pool-1-thread-3,执行:线程2
添加第9个任务
thread name:pool-1-thread-1,执行:线程9

从执行的结果我们可以得知,当执行到第7个任务时,由于线程池拒绝策略,此任务由主线程来执行,当线程池有空闲时,才继续执行其他的任务。所以此策略可能会阻塞主线程。

DiscardPolicy

这种拒绝策略比较简单,线程池拒绝的任务直接抛弃,不会抛异常也不会执行

示例

修改上述的代码,将拒绝策略修改为DiscardPolicy

 ThreadPoolExecutor threadPoolExecutor  = new ThreadPoolExecutor(
                2,
                5,
                10,
                TimeUnit.MICROSECONDS,
                new LinkedBlockingDeque<>(1),
                new ThreadPoolExecutor.CallerRunsPolicy());

执行结果

invoke dealStock success
goodsId:手机
thread name:pool-1-thread-1,执行:线程0
thread name:pool-1-thread-4,执行:线程4
thread name:pool-1-thread-5,执行:线程5
thread name:pool-1-thread-3,执行:线程3
thread name:pool-1-thread-2,执行:线程1
thread name:pool-1-thread-1,执行:线程2

从执行的结果来看只执行了6个任务,其他的任务都被抛弃了。

DiscardOldestPolicy

DiscardOldestPolicy 当任务拒绝添加时,会抛弃任务队列中最先加入队列的任务,再把新任务添加进去。

示例说明

 ThreadPoolExecutor threadPoolExecutor  = new ThreadPoolExecutor(
                1,
                2,
                10,
                TimeUnit.MICROSECONDS,
                new LinkedBlockingDeque<>(2),
                new ThreadPoolExecutor.CallerRunsPolicy());

执行结果:

添加第0个任务
添加第1个任务
添加第2个任务
添加第3个任务
添加第4个任务
添加第5个任务
invoke dealStock success
goodsId:手机
thread name:pool-1-thread-2,执行:线程3
thread name:pool-1-thread-1,执行:线程0
thread name:pool-1-thread-1,执行:线程2
thread name:pool-1-thread-2,执行:线程1

自定义拒绝策略

当线程池提供的拒绝策略无法满足要求时,我们可以采用自定义的拒绝策略,只需要实现RejectedExecutionHandler接口即可

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

  ThreadPoolExecutor threadPoolExecutor  = new ThreadPoolExecutor(
                1,
                2,
                10,
                TimeUnit.MICROSECONDS,
                new LinkedBlockingDeque<>(2),
                new CustRejectedExecutionHandler());

执行结果:

thread name:客户线程:6,执行:线程5
thread name:pool-1-thread-1,执行:线程0
thread name:客户线程:8,执行:线程4
thread name:pool-1-thread-2,执行:线程3
thread name:pool-1-thread-1,执行:线程1
thread name:pool-1-thread-2,执行:线程2

从执行的结果来看,被拒绝的任务都在客户的新线程中执行。

小结

  • AbortPolicy:直接抛出异常,后续的任务不会执行
  • CallerRunsPolicy:子任务执行的时间过长,可能会阻塞主线程。
  • DiscardPolicy:不抛异常,任务直接丢弃
  • DiscardOldestPolicy;丢弃最先加入队列的任务

总结

本文对于线程的池的几种策略进行详细的讲解,在实际的生产中需要集合相关的场景来选择合适的拒绝策略,如有疑问,请随时反馈。

到此这篇关于Java ThreadPoolExecutor的拒绝策略的文章就介绍到这了,更多相关Java ThreadPoolExecutor拒绝策略内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java线程池的拒绝策略实现详解

    一.简介 jdk1.5 版本新增了JUC并发编程包,大大的简化了传统的多线程开发. Java线程池,是典型的池化思想的产物,类似的还有数据库的连接池.redis的连接池等.池化思想,就是在初始的时候去申请资源,创建一批可使用的连接,这样在使用的时候,就不必再进行创建连接信息的开销了.举个生活中鲜明的例子,在去著名洋快餐某基或者某劳的时候,配餐人员是字节从一个中间的保温箱里面直接取,然后打包就好了.不用再临时的来了一个单子,又要去拿原材料,又要去进行加工.效率明显的就是提高了很多. 既然是池子,那

  • java ThreadPoolExecutor线程池拒绝策略避坑

    目录 1.场景 2. 原因分析 3.总结 4.思考 1.场景 线程池使用DiscardOldestPolicy拒绝策略,阻塞队列使用ArrayBlockingQueue,发现在某些情形下对于得到的Future,调用get()方法当前线程会一直阻塞. 为了便于理解,将实际情景抽象为下面的代码: ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 1, 1, 1, TimeUnit.SECONDS, new ArrayBlo

  • java线程池ThreadPoolExecutor的八种拒绝策略示例详解

    目录 池化设计思想 线程池触发拒绝策略的时机 JDK内置4种线程池拒绝策略 拒绝策略接口定义 AbortPolicy(中止策略) DiscardPolicy(丢弃策略) DiscardOldestPolicy(弃老策略) 第三方实现的拒绝策略 Dubbo 中的线程拒绝策略 Netty 中的线程池拒绝策略 ActiveMQ 中的线程池拒绝策略 PinPoint 中的线程池拒绝策略 谈到 Java 的线程池最熟悉的莫过于 ExecutorService 接口了,jdk1.5 新增的 java.uti

  • 详解什么是Java线程池的拒绝策略?

    拒绝策略 (JDK提供了4种,另外也可以自定义拒绝策略,因此总共有5种.) 线程池中的线程已经用完了,无法继续为新任务服务,同时,等待队列也已经排满了,再也塞不下新任务了.这时候我们就需要拒绝策略机制合理的处理这个问题. JDK 内置的拒绝策略如下: 1.AbortPolicy : 直接抛出异常,阻止系统正常运行. 2.CallerRunsPolicy : 只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务.显然这样做不会真的丢弃任务,但是,任务提交线程的性能极有可能会急剧下降.

  • 详解Java ThreadPoolExecutor的拒绝策略

    目录 背景 线程池基本原理 线程池拒绝策略 AbortPolicy CallerRunsPolicy DiscardPolicy 示例 执行结果 DiscardOldestPolicy 示例说明 自定义拒绝策略 小结 总结 背景 线程池的技术在项目中使用广泛,线程池提供了四种拒绝策略,大家是否了解这四种拒绝的策略呢?本文将详细的讲解ThreadPoolExecutor的四种拒绝策略,以及相关的注意事项. 线程池基本原理 线程池的原理如下图: 说明: 当前运行的线程少于corePoolSize,则

  • 详解Java编程中的策略模式

    策略模式属于对象的行为模式.其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换.策略模式使得算法可以在不影响到客户端的情况下发生变化. 策略模式的结构 策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理.策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类.用一句话来说,就是:"准备一组算法,并将每一个算法封装起来,使得它们可以互换".下面就以一个示意性的实现讲解策略模式实例的结构. 这个

  • 详解Java策略模式

    一.策略模式到底是什么? 策略模式属于对象的行为模式.其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换.策略模式使得算法可以在不影响到客户端的情况下发生变化. 简单的说,策略模式代表了一类算法的通用解决方案,你可以在运行时选择使用哪种解决方案. 策略模式的重心 策略模式的重心不是如何实现算法, 而是如何组织.调用这些算法, 从而使得程序结构更加灵活,具有更好的维护性和扩展性. 算法的平等性 策略模式一个很大的特点就是各个策略算法的平等性.对于一系列具体的

  • 详解Java线程池的使用及工作原理

    一.什么是线程池? 线程池是一种用于实现计算机程序并发执行的软件设计模式.线程池维护多个线程,等待由调度程序分配任务以并发执行,该模型提高了性能,并避免了由于为短期任务频繁创建和销毁线程而导致的执行延迟. 二.线程池要解决什么问题? 说到线程池就一定要从线程的生命周期讲起. 从图中可以了解无论任务执行多久,每个线程都要经历从生到死的状态.而使用线程池就是为了避免线程的重复创建,从而节省了线程的New至Runnable, Running至Terminated的时间:同时也会复用线程,最小化的节省系

  • 详解Java线程池是如何重复利用空闲线程的

    在Java开发中,经常需要创建线程去执行一些任务,实现起来也非常方便,但如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间.此时,我们很自然会想到使用线程池来解决这个问题. 使用线程池的好处: 降低资源消耗.java中所有的池化技术都有一个好处,就是通过复用池中的对象,降低系统资源消耗.设想一下如果我们有n多个子任务需要执行,如果我们为每个子任务都创建一个执行线程,而创建线程的过程是需要一定的系统消耗

  • 详解java中的static关键字

    Java中的static关键字可以用于修饰变量.方法.代码块和类,还可以与import关键字联合使用,使用的方式不同赋予了static关键字不同的作用,且在开发中使用广泛,这里做一下深入了解. 静态资源(静态变量与静态方法) 被static关键字修饰的变量和方法统一属于类的静态资源,是类实例之间共享的.被static关键字修饰的变量.方法属于类变量.类方法,可以通过[类名.变量名].[类名.方法名]直接引用,而不需要派生一个类实例出来. 静态资源分类存放的好处 JDK把不同的静态资源放在了不同的

  • 详解Java分布式事务的 6 种解决方案

    介绍 在分布式系统.微服务架构大行其道的今天,服务间互相调用出现失败已经成为常态.如何处理异常,如何保证数据一致性,成为微服务设计过程中,绕不开的一个难题. 在不同的业务场景下,解决方案会有所差异,常见的方式有: 阻塞式重试: 2PC.3PC 传统事务: 使用队列,后台异步处理: TCC 补偿事务: 本地消息表(异步确保): MQ 事务. 本文侧重于其他几项,关于 2PC.3PC 传统事务,网上资料已经非常多了,这里不多做重复. 阻塞式重试 在微服务架构中,阻塞式重试是比较常见的一种方式.伪代码

  • 一文详解Java中的类加载机制

    目录 一.前言 二.类加载的时机 2.1 类加载过程 2.2 什么时候类初始化 2.3 被动引用不会初始化 三.类加载的过程 3.1 加载 3.2 验证 3.3 准备 3.4 解析 3.5 初始化 四.父类和子类初始化过程中的执行顺序 五.类加载器 5.1 类与类加载器 5.2 双亲委派模型 5.3 破坏双亲委派模型 六.Java模块化系统 一.前言 Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最 终形成可以被虚拟机直接使用的Java类型,这个过程

  • 详解Java中的OkHttp JSONP爬虫

    目录 什么是JSOUP 什么是OkHttp 爬虫需要掌握的技术 需要的依赖 JSON入门Demo JSOUP常用方法 使用JSOUP 方式连接 User-Agent(随机) 后台爬虫的三大问题 selenium+phantomjs(维护中…内容重新整理) 什么是JSOUP JSOUP 是一款Java 的HTML解析器,可直接解析某个URL地址.HTML文本内容.它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据. 官网 jsoup实现了WHATWG

  • 详解Java中多线程异常捕获Runnable的实现

    详解Java中多线程异常捕获Runnable的实现 1.背景: Java 多线程异常不向主线程抛,自己处理,外部捕获不了异常.所以要实现主线程对子线程异常的捕获. 2.工具: 实现Runnable接口的LayerInitTask类,ThreadException类,线程安全的Vector 3.思路: 向LayerInitTask中传入Vector,记录异常情况,外部遍历,判断,抛出异常. 4.代码: package step5.exception; import java.util.Vector

随机推荐