线程池超载求生指南:四大拒绝策略应对并发挑战(Java线程池拒绝策略全解析)

文章目录

  • 线程池拒绝策略:优雅处理过载请求
    • 什么是线程池拒绝策略?
    • 内置的拒绝策略
    • 代码示例
      • 创建一个简单的线程池
      • 使用 AbortPolicy
      • 使用 CallerRunsPolicy
      • 使用 DiscardPolicy
      • 使用 DiscardOldestPolicy
    • 通俗易懂地理解这些拒绝策略
    • 自定义拒绝策略
    • 总结

线程池拒绝策略:优雅处理过载请求

在现代软件开发中,线程池(ThreadPool)是一个常用的并发处理工具。它能有效地管理和复用线程资源,提升系统的性能和响应速度。然而,当线程池达到最大容量时,会出现任务无法提交的情况,这时需要处理这种情况的机制称为“拒绝策略”(Rejection Policy)。本文将详细讲述 Java 中的线程池拒绝策略,帮助读者理解其原理和应用场景。

什么是线程池拒绝策略?

线程池拒绝策略是指当线程池中的任务数量达到上限时,新提交的任务如何处理的一种策略。Java 提供了几种内置的拒绝策略,开发者也可以自定义策略。

内置的拒绝策略

在Java中,java.util.concurrent.ThreadPoolExecutor类提供了四种预定义的拒绝策略,它们分别是:

  1. AbortPolicy:这是默认的拒绝策略。当新任务到来但无法被处理时,会抛出一个RejectedExecutionException异常,并关闭调用者提供的RunnableCallable对象的Future,如果适用的话。
  2. CallerRunsPolicy:这个策略不会丢弃任务,也不会创建新的线程,而是将任务回退给调用者,由调用者的线程直接执行该任务。这可能会导致调用者的线程阻塞,直到任务完成。
  3. DiscardOldestPolicy:如果无法执行新任务,则会从任务队列中移除最老的任务,然后尝试再次提交新任务。这种策略可以防止队列无限制地增长,但是可能会导致某些任务永远无法被执行。
  4. DiscardPolicy:简单地丢弃无法处理的任务,不抛出异常,也不通知调用者。这种策略可以避免系统因异常而崩溃,但同时也意味着某些重要任务可能被静默地丢弃,这在某些场景下可能是不可接受的。
    每种策略都有其适用场景,选择哪种策略取决于应用程序的具体需求和对失败任务的容忍度。例如,在资源有限且需要保证系统稳定性的环境中,DiscardPolicyDiscardOldestPolicy可能更为合适;而在需要确保每个任务都被处理的情况下,CallerRunsPolicy可能是更好的选择。

为了设置线程池的拒绝策略,可以通过ThreadPoolExecutor构造函数中的RejectedExecutionHandler参数来指定,或者在创建线程池后通过setRejectedExecutionHandler()方法来修改。

代码示例

创建一个简单的线程池

import java.util.concurrent.*;public class ThreadPoolExample {private static final int CORE_POOL_SIZE = 2;private static final int MAX_POOL_SIZE = 4;private static final long KEEP_ALIVE_TIME = 10L;public static void main(String[] args) {// 使用 ArrayBlockingQueue 作为任务队列,容量为 2BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);// 创建线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(CORE_POOL_SIZE,MAX_POOL_SIZE,KEEP_ALIVE_TIME,TimeUnit.SECONDS,queue);// 提交任务for (int i = 0; i < 10; i++) {final int taskNumber = i + 1;executor.submit(() -> {try {System.out.println("Executing task " + taskNumber);Thread.sleep(2000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}});}// 关闭线程池executor.shutdown();}
}

使用 AbortPolicy

import java.util.concurrent.*;public class AbortPolicyExample {public static void main(String[] args) {BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 10L, TimeUnit.SECONDS, queue,new ThreadPoolExecutor.AbortPolicy() // 使用 AbortPolicy);for (int i = 0; i < 10; i++) {final int taskNumber = i + 1;try {executor.submit(() -> {try {System.out.println("Executing task " + taskNumber);Thread.sleep(2000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}});} catch (RejectedExecutionException e) {System.err.println("Task " + taskNumber + " was rejected");}}executor.shutdown();}
}

使用 CallerRunsPolicy

import java.util.concurrent.*;public class CallerRunsPolicyExample {public static void main(String[] args) {BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 10L, TimeUnit.SECONDS, queue,new ThreadPoolExecutor.CallerRunsPolicy() // 使用 CallerRunsPolicy);for (int i = 0; i < 10; i++) {final int taskNumber = i + 1;executor.submit(() -> {System.out.println("Executing task " + taskNumber);try {Thread.sleep(2000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}});}executor.shutdown();}
}

使用 DiscardPolicy

import java.util.concurrent.*;public class DiscardPolicyExample {public static void main(String[] args) {BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 10L, TimeUnit.SECONDS, queue,new ThreadPoolExecutor.DiscardPolicy() // 使用 DiscardPolicy);for (int i = 0; i < 10; i++) {final int taskNumber = i + 1;executor.submit(() -> {System.out.println("Executing task " + taskNumber);try {Thread.sleep(2000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}});}executor.shutdown();}
}

使用 DiscardOldestPolicy

import java.util.concurrent.*;public class DiscardOldestPolicyExample {public static void main(String[] args) {BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 10L, TimeUnit.SECONDS, queue,new ThreadPoolExecutor.DiscardOldestPolicy() // 使用 DiscardOldestPolicy);for (int i = 0; i < 10; i++) {final int taskNumber = i + 1;executor.submit(() -> {System.out.println("Executing task " + taskNumber);try {Thread.sleep(2000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}});}executor.shutdown();}
}

通俗易懂地理解这些拒绝策略

为了更好地理解这些拒绝策略,我们可以将其类比于生活中的场景:

  1. AbortPolicy:就像餐厅已满员,不再接待新客人,并告知客人“已经客满,请去别处”。
  2. CallerRunsPolicy:就像餐厅忙不过来时,老板自己上阵服务客人,保证所有客人都能被服务到。
  3. DiscardPolicy:就像餐厅已满员,直接不理会新来的客人,不告知任何信息。
  4. DiscardOldestPolicy:就像餐厅已满员,把最早来但还没点菜的客人请走,以便接待新来的客人。
    自定义拒绝策略

自定义拒绝策略

除了内置的拒绝策略,开发者还可以根据实际需求自定义拒绝策略。例如,记录日志、发送通知等。

import java.util.concurrent.*;public class CustomRejectionPolicyExample {public static void main(String[] args) {BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 10L, TimeUnit.SECONDS, queue,new RejectedExecutionHandler() {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {System.err.println("Task " + r.toString() + " was rejected");// 这里可以添加更多处理逻辑,比如记录日志、发送通知等}});for (int i = 0; i < 10; i++) {final int taskNumber = i + 1;executor.submit(() -> {System.out.println("Executing task " + taskNumber);try {Thread.sleep(2000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}});}executor.shutdown();}
}

总结

线程池拒绝策略概念
线程池拒绝策略是在线程池达到最大容量且任务队列满负荷时,用于处理额外任务的方法。这在并发编程中至关重要,因为它帮助系统在高负载情况下保持稳定性和可预测性。

内置拒绝策略详解
Java的ThreadPoolExecutor提供了四种主要的内置拒绝策略:

  1. AbortPolicy:默认策略,当无法执行新任务时,抛出RejectedExecutionException,并关闭相关Future,适用于需要立即反馈不可执行情况的场景。
  2. CallerRunsPolicy:调用者线程将直接执行任务,避免了任务的丢失,但可能导致调用者线程阻塞。
  3. DiscardOldestPolicy:移除队列中最旧的任务,为新任务腾出空间,适用于队列长度控制和优先级管理。
  4. DiscardPolicy:简单地丢弃新任务而不抛出异常,适用于对任务丢失容忍度较高的场景,以避免系统因异常处理而加重负担。

代码示例

  • 创建线程池:通过ThreadPoolExecutor构造函数指定核心线程数、最大线程数、空闲线程存活时间、任务队列类型和容量以及拒绝策略。
  • 使用不同策略:示例代码展示了如何分别使用AbortPolicy、CallerRunsPolicy、DiscardPolicy和DiscardOldestPolicy,以及它们在任务过载时的行为差异。

生活场景类比
为了加深理解,上述策略被类比于餐厅管理场景,形象说明了不同策略下的处理方式。

自定义拒绝策略
开发者可以根据具体需求实现RejectedExecutionHandler接口来自定义拒绝策略,例如记录日志、发送警报邮件或执行其他业务逻辑。

应用场景指导

  • 在资源有限的环境下,选择DiscardPolicyDiscardOldestPolicy以维持系统稳定性。
  • 当每个任务都需要被处理时,使用CallerRunsPolicy确保任务的执行,尽管可能会牺牲调用者线程的响应能力。

结论
合理选择和配置线程池的拒绝策略对于构建高效、健壮的并发系统至关重要。开发者应根据系统需求和预期行为精心挑选策略,以达到最佳的并发处理效果。

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

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

相关文章

5.Fabric的共识机制

在Fabric中,有以下3中典型共识机制。 Solo共识 solo共识机制只能用于单节点模式,即只能有一个Orderer节点,因此,其共识过程很简单,每接收到一个交易信息,就在共识模块的控制下产生区块并广播给节点存储到账本中。 Solo 模式下的共识只适用于一个Orderer节点,所以可以在…

C/C++ 内存管理

C/C 内存管理 1. C/C内存分布2. C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free3. C内存管理方式3.1 new/delete操作内置类型3.2 new和delete操作自定义类型 4. operator new与operator delete函数&#xff08;重要点进行讲解&#xff09;4.1 operator new与o…

【Java】:洗牌功能和杨辉三角的实现

洗牌 此操作包含的基本功能有&#xff1a; 组牌&#xff1a;组建 52 张扑克牌 四种花色&#xff1a;“♥️”&#xff0c;“♠️”&#xff0c;“⬛️”&#xff0c;“♣️”每种花色 13 张牌&#xff1a;1~13 洗牌&#xff1a;将 52 张扑克牌打乱顺序发牌&#xff1a;给三个人…

【深度学习入门篇 ⑪】自注意力机制

【&#x1f34a;易编橙&#xff1a;一个帮助编程小伙伴少走弯路的终身成长社群&#x1f34a;】 大家好&#xff0c;我是小森( &#xfe61;ˆoˆ&#xfe61; ) &#xff01; 易编橙终身成长社群创始团队嘉宾&#xff0c;橙似锦计划领衔成员、阿里云专家博主、腾讯云内容共创官…

C#进阶:深入理解异步编程与并发

在现代软件开发中&#xff0c;异步编程和并发控制是不可或缺的技能。随着应用程序变得越来越复杂&#xff0c;对性能、响应性和可扩展性的要求也越来越高。C#提供了强大的工具来支持异步编程和并发处理&#xff0c;如async和await关键字、Task Parallel Library&#xff08;TPL…

Vue3 SvgIcon组件开发

在前面自定义tree组件继续功能迭代前&#xff0c;我们先开发一个通用的ScgIcon组件&#xff0c;用于后续组件模板中小图标的展示。 引入iconfont 官网&#xff1a;https://www.iconfont.cn/ 选取图标进行下载&#xff0c;只取iconfont.js文件 在prettier中忽略该文件&#x…

【YOLOv5/v7改进系列】引入CoordConv——坐标卷积

一、导言 与标准卷积层相比&#xff0c;CoordConv 的主要区别在于它显式地考虑了位置信息。在标准卷积中&#xff0c;卷积核在输入上滑动时&#xff0c;仅关注局部区域的像素强度&#xff0c;而忽略其绝对位置。CoordConv 通过在输入特征图中添加坐标信息&#xff0c;使得卷积…

【常用知识点-Linux】查询端口情况

Author&#xff1a;赵志乾 Date&#xff1a;2024-07-22 Declaration&#xff1a;All Right Reserved&#xff01;&#xff01;&#xff01; 1. 简介 ss命令为socket statistics的缩写&#xff0c;是Linux的一个网络管理命令&#xff0c;主要用于获取系统中socket的统计信息&am…

[C/C++入门][for]26、统计满足条件的4位数(循环经典练习)

给定若干个四位数&#xff0c;求出其中满足以下条件的数的个数&#xff1a;个位数上的数字减去千位数上的数字&#xff0c;再减去百位数上的数字&#xff0c;再减去十位数上的数字的结果大于零。 【输入】 输入为两行&#xff0c;第一行为四位数的个数n&#xff0c;第二行为n个…

【SpringBoot】第3章 系统配置之日志配置

SpringBoot自带spring-boot-starter-logging库来实现系统日志功能&#xff0c;spring-boot-starter-logging组件默认使用LogBack日志记录工具。系统运行日志默认输出到控制台&#xff0c;也能输出到文件中。下面通过示例来演示SpringBoot项目配置日志的功能。 修改pom.xml文件…

校园招聘 之 Java HashMap

校园招聘与社会招聘在多个方面存在显著的区别&#xff0c;这些区别主要体现在招聘对象、招聘目的、招聘方式、招聘周期、招聘成本以及入职后的发展等方面。校招也更注重理论知识&#xff0c;俗称八股文&#xff0c;其实有些东西为什么一直拿来问&#xff0c;其实这里面你仔细品…

速盾:cdn技术实现原理是什么?

CDN技术&#xff08;内容分发网络&#xff09;是一种通过将内容部署到离用户更近的服务器上&#xff0c;从而提高网站访问速度和用户体验的技术。它的实现原理主要包括以下几个步骤&#xff1a; 域名解析&#xff1a;用户输入网址&#xff0c;浏览器首先向DNS服务器发送域名解析…

STM32CubeIDE(CAN)

目录 一、概念 1、简述 2、CAN 的几种模式 二、实践 1、环回模式轮询通信 1.1 软件配置 1.2 代码编写 2、环回模式中断通信 2.1 软件配置 2.2 代码编写 一、概念 1、简述 STM32微控制器系列包含多个型号&#xff0c;其中一些型号集成了CAN&#xff08;Controller Are…

Vuex--全局共享数据

目录 一 是什么? 二 怎么用&#xff1f; 三 注意点 一 是什么? 在此之前&#xff0c;我们使用vue的数据全部放在每个组件的data区域里面&#xff0c;这里return里面存的都是这个组件要用到的数据&#xff0c;但是这里面的数据是局部的数据&#xff0c;也就是说这些数据是这…

Chrome v8 pwn 前置

文章目录 参考用到啥再更新啥简介环境搭建depot_tools和ninjaturbolizer 调试turbolizer使用结构数组 ArrayArrayBufferDataViewWASMJSObject结构Hidden Class命名属性-快速属性Fast Properties命名属性-慢速属性Slow Properties 或 字典模式Dictionary Mode编号属性 (Elements…

基于springboot+vue+uniapp的宿舍管理系统小程序

开发语言&#xff1a;Java框架&#xff1a;springbootuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#…

van-dialog 组件调用报错

报错截图 报错原因 这个警告表明 vue 在渲染页面时遇到了一个未知的自定义组件 <van-dialog>&#xff0c;并且提示可能是由于未正确注册该组件导致的。在 vue 中&#xff0c;当我们使用自定义组件时&#xff0c;需要先在 vue 实例中注册这些组件&#xff0c;以便 vue 能…

交易积累-AR

在股票交易分析中&#xff0c;AR&#xff08;Accumulation/Distribution Rating&#xff0c;积累/分配指标&#xff09;是一个反映股票在一定时期内被积累&#xff08;买入&#xff09;或分配&#xff08;卖出&#xff09;情况的指标。它是由美国著名的股票投资者威廉J奥尼尔&a…

本地搭建ros2环境步骤(x86_64架构)

验证效果&#xff1a;ros2跟redis能够相互通信&#xff0c;但不会接收到其它 orin 的 topic 消息&#xff1b;本地的话直接当作slave机器来用 注&#xff1a;ros2 的全局 param 需要依赖一个单点 redis-server&#xff0c;ros2 的全局 param 数据是保存在这个单点 redis-ser…

Json结构解析比较

文章目录 前言正文一、项目简介二、核心代码1、 JavaBeanParser2、 JsonStructCompare3、 Client 测试结果 前言 本次练习&#xff0c;主要是针对于两个Json的结构差异。 多用于测试场景&#xff0c;比如一个很大的Json报文&#xff0c;需要和现有的Json报文对比&#xff0c;看…