Java线程池ThreadPoolExcutor源码解读详解09-4种拒绝策略

本文涉及的产品
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
应用实时监控服务-应用监控,每月50GB免费额度
可观测可视化 Grafana 版,10个用户账号 1个月
简介: 本文介绍了线程池的四种拒绝策略:AbortPolicy、DiscardPolicy、DiscardOldestPolicy和CallerRunsPolicy,并通过代码示例展示了它们在任务过多时的不同处理方式。AbortPolicy会抛出异常并停止主线程;DiscardPolicy会默默丢弃新任务;DiscardOldestPolicy会抛弃队列中最旧的任务来接纳新任务;而CallerRunsPolicy则是由调用者线程执行被拒绝的任务,以减缓新任务的提交速度。这四种策略适用于不同的场景,开发者可以根据需求选择合适的策略。

💪🏻 制定明确可量化的目标,坚持默默的做事。





一、继承实现关系图

线程池的四种拒绝策略实现关系图:

image.gif image.png

  • AbortPolicy(默认):直接抛出RejectedExecutionException异常并且阻止主线程正常运行
  • DiscardPolicy:直接丢弃任务,不予任何处理也不抛出异常
  • DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务
  • CallerRunsPolicy:“调用者运行“一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,让调用者去处理,比如main调用了线程池,线程池处理不了就让main慢慢处理去,从而降低新任务的流量

二、拒绝策略测试示例

// 测试任务类
class Task implements Runnable {
        private String name;
        public Task(String name) {
            this.name = name;
        }
        @Override
        public void run() {
            try {
                Thread.sleep(1100);
                System.out.println("thread name: " + this.name);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

image.gif

2.1 AbortPolicy策略

测试代码块:

// ThreadPoolExcecutor类中
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
// 测试类
private void abortPolicy() {
    ThreadFactory factory = r -> new Thread(r, "AbortPolicy ThreadFactory");
    // 创建核心线程和最大线程为2的线程池
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
            2,
            1,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(2), factory);
    int i = 0;
    int n = 100;
    // 添加100个任务
    while (i++ < n) {
        threadPoolExecutor.execute(new ThreadPolicyTester.Task("" + i));
    }
}

image.gif

运行如上代码打印如下:

Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task w.cx.lrn.java.interview.threadpool.ThreadPolicyTester$Task@4d405ef7 rejected from java.util.concurrent.ThreadPoolExecutor@6193b845[Running, pool size = 2, active threads = 2, queued tasks = 2, completed tasks = 0]
  at java.base/java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2065)
  at java.base/java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:833)
  at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1365)
  at w.cx.lrn.java.interview.threadpool.ThreadPolicyTester.abortPolicy(ThreadPolicyTester.java:34)
  at w.cx.lrn.java.interview.threadpool.ThreadPolicyTester.main(ThreadPolicyTester.java:18)
thread name: 1
thread name: 2
thread name: 3
thread name: 4

image.gif

说明:

  • 没有指定拒绝策略,默认为AbortPolicy拒绝策略
  • 最大线程数为2,则有两个任务在运行
  • 阻塞队列数为2,则阻塞队列中可添加2个任务
  • 添加第五个任务的时候抛出了 RejectedExecutionException异常并且阻止主线程正常运行(没有再尝试添加第六个线程),所以只打印前4个任务1,2,3和4任务

2.2 DiscardPolicy策略

测试代码块:(添了拒绝策略)

private void discardPolicy() {
    ThreadFactory factory2 = r -> new Thread(r, "DiscardPolicy ThreadFactory");
    // 核心线程数和、最大线程数 和 阻塞队列大小均为2
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
            2,
            1,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(2), factory2, new ThreadPoolExecutor.DiscardPolicy());
    int i = 0;
    int n = 10;
    // 添加10个任务
    while (i++ < n) {
        threadPoolExecutor.execute(new ThreadPolicyTester.Task("" + i));
        System.out.println("添加了第 " + i + " 个任务!");
    }
}

image.gif

运行如上代码打印如下:

添加了第 1 个任务!
添加了第 2 个任务!
添加了第 3 个任务!
添加了第 4 个任务!
添加了第 5 个任务!
添加了第 6 个任务!
添加了第 7 个任务!
添加了第 8 个任务!
添加了第 9 个任务!
添加了第 10 个任务!
thread name: 1
thread name: 2
thread name: 4
thread name: 3

image.gif

说明:

  • 指定拒绝策略为DiscardPolicy
  • 最大线程数为2,则有两个任务在运行
  • 阻塞队列数为2,则阻塞队列中可添加2个任务
  • 添加第5个到第10个任务都不予任何处理也不抛出异常,所以只打印前4个任务1,2,3和4任务

2.3 DiscardOldestPolicy策略

测试代码块:(添了DiscardOldestPolicy拒绝策略)

private void discardOldestPolicy() {
    ThreadFactory factory2 = r -> new Thread(r, "DiscardOldestPolicy ThreadFactory");
    // 核心线程数、最大线程数 和 阻塞队列大小均为2
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
            2,
            0,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(2), factory2, new ThreadPoolExecutor.DiscardOldestPolicy());
    int i = 0;
    int n = 10;
    // 添加10个任务
    while (i++ < n) {
        threadPoolExecutor.execute(new ThreadPolicyTester.Task("" + i));
        System.out.println("添加了第 " + i + " 个任务!");
    }
}

image.gif

运行如上代码打印如下:

添加了第 1 个任务!
添加了第 2 个任务!
添加了第 3 个任务!
添加了第 4 个任务!
添加了第 5 个任务!
添加了第 6 个任务!
添加了第 7 个任务!
添加了第 8 个任务!
添加了第 9 个任务!
添加了第 10 个任务!
thread name: 1
thread name: 2
thread name: 9
thread name: 10

image.gif

说明:

  • 指定拒绝策略为DiscardOldestPolicy
  • 最大线程数为2,则有两个任务在运行
  • 阻塞队列数为2,则阻塞队列中可添加2个任务
  • 拒绝策略为DiscardOldestPolicy,添加第5个时,则抛弃第3个任务添加第5个任务,添加第6个任务时,抛弃第4个任务添加第6个任务,依次类推最后阻塞队列中存放的任务为第9个和第10个,所以最后只打印前2个任务1,2和9、10任务

2.4 CallerRunsPolicy策略

测试代码块:(添了CallerRunsPolicy拒绝策略)

private void callerRunsPolicy() {
    ThreadFactory factory2 = r -> new Thread(r, "TestThreadPool");
    // 核心线程数、最大线程数 和 阻塞队列大小均为2
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
            2,
            0,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(2), factory2, new ThreadPoolExecutor.CallerRunsPolicy());
    int i = 0;
    int n = 10;
    // 添加10个任务
    while (i++ < n) {
        threadPoolExecutor.execute(new ThreadPolicyTester.Task("" + i));
        System.out.println("添加了第 " + i + " 个任务!");
    }
}

image.gif

为了方便辨别是主线程执行还是子线程执行,测试任务类的打印修改成如下:

 

System.out.println("thread name: " + Thread.currentThread().getName() + " " + this.name);

image.gif

运行如上代码打印如下:

添加了第 1 个任务!
添加了第 2 个任务!
添加了第 3 个任务!
添加了第 4 个任务!
thread name: callerRunsPolicy  2
thread name: callerRunsPolicy  1
thread name: main 5
添加了第 5 个任务!
添加了第 6 个任务!
添加了第 7 个任务!
thread name: callerRunsPolicy  3
thread name: main 8
添加了第 8 个任务!
添加了第 9 个任务!
thread name: callerRunsPolicy  4
thread name: callerRunsPolicy  6
thread name: main 10
thread name: callerRunsPolicy  7
添加了第 10 个任务!
thread name: callerRunsPolicy  9

image.gif

说明:(多线程环境下运行,每次打印都不一样)

  • 指定拒绝策略为CallerRunsPolicy
  • 最大线程数为2,则有两个任务在运行
  • 阻塞队列数为2,则阻塞队列中可添加2个任务
  • 10任务都打印出来了,被拒绝的第5、8 和 10任务是由主线程执行的,拒绝策略源码如下。就是说添加新任务被拒绝后,只要主线程创建的线程池没有关闭,由主线程来执行被拒绝的新任务。只有线程池关闭了,被拒绝的任务才被丢弃且不抛异常。

拒绝策略源码:

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
        r.run();
    }
}

image.gif

三、总结及应用场景

  • AbortPolicy拒绝策略:新任务不能再添加的时候,会直接抛出异常及时反馈程序运行状态,在一些比较重要的业务中,使用此策略在不能再处理更多的并发量的时候,就能及时的通过异常发现问题。
  • DiscardPolicy拒绝策略:在不能再添加新任务时此策略直接丢弃任务。比如在一些无关紧要的任务可用此策略。
  • DiscardOldestPolicy拒绝策略:新任务替换老任务。应用于任务分配有一定的容忍性的情况,弃用最老的任务以容纳新的任务,一般是可以接受的情况下。
  • CallerRunsPolicy拒绝策略:只要主线程创建的线程池没有关闭,则由主线程来执行任务。这种由主线程来一个一个执行被拒绝新任务,从而实现降低新任务的提交速度。
相关文章
|
1月前
|
XML Java 编译器
Java注解的底层源码剖析与技术认识
Java注解(Annotation)是Java 5引入的一种新特性,它提供了一种在代码中添加元数据(Metadata)的方式。注解本身并不是代码的一部分,它们不会直接影响代码的执行,但可以在编译、类加载和运行时被读取和处理。注解为开发者提供了一种以非侵入性的方式为代码提供额外信息的手段,这些信息可以用于生成文档、编译时检查、运行时处理等。
71 7
|
1月前
|
监控 算法 Java
Java虚拟机(JVM)垃圾回收机制深度剖析与优化策略####
本文作为一篇技术性文章,深入探讨了Java虚拟机(JVM)中垃圾回收的工作原理,详细分析了标记-清除、复制算法、标记-压缩及分代收集等主流垃圾回收算法的特点和适用场景。通过实际案例,展示了不同GC(Garbage Collector)算法在应用中的表现差异,并针对大型应用提出了一系列优化策略,包括选择合适的GC算法、调整堆内存大小、并行与并发GC调优等,旨在帮助开发者更好地理解和优化Java应用的性能。 ####
56 0
|
15天前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
72 17
|
13天前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
|
1月前
|
存储 JavaScript 前端开发
基于 SpringBoot 和 Vue 开发校园点餐订餐外卖跑腿Java源码
一个非常实用的校园外卖系统,基于 SpringBoot 和 Vue 的开发。这一系统源于黑马的外卖案例项目 经过站长的进一步改进和优化,提供了更丰富的功能和更高的可用性。 这个项目的架构设计非常有趣。虽然它采用了SpringBoot和Vue的组合,但并不是一个完全分离的项目。 前端视图通过JS的方式引入了Vue和Element UI,既能利用Vue的快速开发优势,
130 13
|
1月前
|
存储 监控 小程序
Java中的线程池优化实践####
本文深入探讨了Java中线程池的工作原理,分析了常见的线程池类型及其适用场景,并通过实际案例展示了如何根据应用需求进行线程池的优化配置。文章首先介绍了线程池的基本概念和核心参数,随后详细阐述了几种常见的线程池实现(如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等)的特点及使用场景。接着,通过一个电商系统订单处理的实际案例,分析了线程池参数设置不当导致的性能问题,并提出了相应的优化策略。最终,总结了线程池优化的最佳实践,旨在帮助开发者更好地利用Java线程池提升应用性能和稳定性。 ####
|
2月前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
65 12
|
1月前
|
JavaScript 安全 Java
java版药品不良反应智能监测系统源码,采用SpringBoot、Vue、MySQL技术开发
基于B/S架构,采用Java、SpringBoot、Vue、MySQL等技术自主研发的ADR智能监测系统,适用于三甲医院,支持二次开发。该系统能自动监测全院患者药物不良反应,通过移动端和PC端实时反馈,提升用药安全。系统涵盖规则管理、监测报告、系统管理三大模块,确保精准、高效地处理ADR事件。
|
1月前
|
监控 Java 开发者
深入理解Java中的线程池实现原理及其性能优化####
本文旨在揭示Java中线程池的核心工作机制,通过剖析其背后的设计思想与实现细节,为读者提供一份详尽的线程池性能优化指南。不同于传统的技术教程,本文将采用一种互动式探索的方式,带领大家从理论到实践,逐步揭开线程池高效管理线程资源的奥秘。无论你是Java并发编程的初学者,还是寻求性能调优技巧的资深开发者,都能在本文中找到有价值的内容。 ####
|
2月前
|
运维 Java 编译器
Java 异常处理:机制、策略与最佳实践
Java异常处理是确保程序稳定运行的关键。本文介绍Java异常处理的机制,包括异常类层次结构、try-catch-finally语句的使用,并探讨常见策略及最佳实践,帮助开发者有效管理错误和异常情况。
129 5