在Spring Boot中配置@Async的线程池的拒绝策略

在上一篇文章中,我们使用多个线程隔离不同的异步任务,这篇文章,我们将围绕在@Async的线程池的拒绝策略进行完善线程池的使用,在我们例举案例之前,我们先了解一下:


  1. @Async的拒绝策略用来解决什么问题,还有使用他究竟有什么好处?

    使用@Async的拒绝策略可以解决异步任务线程池队列已满时的问题。当线程池队列已满时,默认的拒绝策略是抛出RejectedExecutionException异常,表示无法接受新的任务。而自定义拒绝策略可以提供一种灵活的方式来处理这种情况,从而解决以下问题:

    1. 避免任务丢失:当线程池队列已满时,如果没有合适的拒绝策略,新的任务可能会被丢弃,导致任务丢失。通过自定义拒绝策略,你可以选择将任务丢弃、阻塞等待或者采取其他适当的处理方式,以避免任务丢失。

    2. 控制任务流量:拒绝策略可以帮助你控制任务的流量。当线程池队列已满时,你可以选择拒绝执行新的任务,从而控制任务的提交速率,避免系统资源被过度消耗。这对于保护系统的稳定性和可靠性非常重要。

    3. 提供反馈机制:自定义拒绝策略可以提供一种反馈机制,告知任务提交者任务被拒绝执行的原因。通过捕获拒绝执行的异常或其他方式,你可以根据需要记录日志、发送通知或采取其他适当的操作,以便及时了解任务无法执行的情况。


  1. 在什么情况下,我们才使用@Async的拒绝策略?
    1. 任务队列满载:当异步任务提交的速度超过线程池处理任务的速度时,任务队列可能会被填满。这时,新的任务无法加入队列,就需要使用线程拒绝策略来处理这些被拒绝的任务。

    2. 任务执行资源有限:当系统的资源(如线程数)有限,并且无法扩展时,可能会出现无法处理所有任务的情况。这时,使用线程拒绝策略可以控制任务的提交速率,避免资源被过度消耗。

    3. 任务处理能力不足:当异步任务的处理能力不足以满足需求时,可以使用线程拒绝策略来限制任务的提交,以避免任务堆积和系统负载过高。

    4. 任务优先级管理:有时,你可能希望根据任务的优先级来管理任务的执行。通过自定义线程拒绝策略,你可以根据任务的优先级进行选择性的拒绝执行,以确保高优先级任务能够及时得到处理。

接下来,我们看一个案例:
我们先创建一个Spring Boot应用,创建好我们的线程池配置。

@EnableAsync
@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}@EnableAsync@Configurationclass TaskPoolConfig {@Beanpublic Executor taskExecutor1() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(2);executor.setMaxPoolSize(2);executor.setQueueCapacity(2);executor.setKeepAliveSeconds(60);executor.setThreadNamePrefix("executor-1-");//后续在这里填写我们需要的拒绝策略return executor;}}
}

如上,我们创建了一个核心线程数为2,最大线程数为2,缓冲队列长度为2,假设我们有五个异步任务同时开始,那么会造成什么情况呢?

接着看吧,我们使用@Async注解实现一个任务

@Slf4j
@Component
public class AsyncTasks {public static Random random = new Random();@Async("taskExecutor1")public CompletableFuture<String> doTaskOne(String taskNo) throws Exception {log.info("开始任务:{}", taskNo);long start = System.currentTimeMillis();Thread.sleep(random.nextInt(10000));long end = System.currentTimeMillis();log.info("完成任务:{},耗时:{} 毫秒", taskNo, end - start);return CompletableFuture.completedFuture("任务完成");}
}

我们来编写一个测试用例,来看看会发生什么结果?

@Slf4j
@SpringBootTest
public class ApplicationTests {@Autowiredprivate AsyncTasks asyncTasks;@Testpublic void test2() throws Exception {long start = System.currentTimeMillis();// 线程池1CompletableFuture<String> task1 = asyncTasks.doTaskOne("1");CompletableFuture<String> task2 = asyncTasks.doTaskOne("2");CompletableFuture<String> task3 = asyncTasks.doTaskOne("3");CompletableFuture<String> task4 = asyncTasks.doTaskOne("4");CompletableFuture<String> task5 = asyncTasks.doTaskOne("5");// 一起执行CompletableFuture.allOf(task1, task2, task3, task4, task5).join();long end = System.currentTimeMillis();log.info("任务全部完成,总耗时:" + (end - start) + "毫秒");}
}
2023-11-28 19:03:57.138  INFO 27916 --- [   executor-1-1] com.miaow.demo.AsyncTasks       : 开始任务:1
2023-11-28 19:03:57.138  INFO 27916 --- [   executor-1-2] com.miawo.demo.AsyncTasks       : 开始任务:2org.springframework.core.task.TaskRejectedException: Executor [java.util.concurrent.ThreadPoolExecutor@5580d62f[Running, pool size = 2, active threads = 2, queued tasks = 2, completed tasks = 0]] did not accept task: java.util.concurrent.CompletableFuture$AsyncSupply@17b6d426at org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor.execute(ThreadPoolTaskExecutor.java:324)at java.util.concurrent.CompletableFuture.asyncSupplyStage(CompletableFuture.java:1618)at java.util.concurrent.CompletableFuture.supplyAsync(CompletableFuture.java:1843)at org.springframework.aop.interceptor.AsyncExecutionAspectSupport.doSubmit(AsyncExecutionAspectSupport.java:274)...at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.CompletableFuture$AsyncSupply@17b6d426 rejected from java.util.concurrent.ThreadPoolExecutor@5580d62f[Running, pool size = 2, active threads = 2, queued tasks = 2, completed tasks = 0]at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)at org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor.execute(ThreadPoolTaskExecutor.java:321)... 76 more

我们来对报错日志进行分析:
[java.util.concurrent.ThreadPoolExecutor@5580d62f[Running, pool size = 2, active threads = 2, queued tasks = 2, completed tasks = 0]] did not accept task: 这段代码中,我们可以明确的知道,在我们的超过了执行线程 + 缓冲队列长度,也就是 2 + 2 = 4 ,但是我们进来了5个线程,所以我们的第五个线程就被拒绝了。
所以,在默认情况之下,我们的线程池的拒绝策略就是:
当线程池队列满了,那么我们的线程池就会丢弃这个任务,并抛出异常


OK,既然线程池中有默认的线程池拒绝策略,那么我们可以对他配置吗?考虑到实际开发过程中,我们在有些任务场景中,直接拒绝的策略一般都不太适用,有的时候,我们会选择丢掉之前开始执行但是并未完成的任务,也可能会考虑丢掉刚刚开始执行,但是没完成的任务,反正有各种场景,只要你线程没执行完毕,我就可以丢弃你,那么我们具体要怎么实现呢?

线程池的拒绝策略是指当线程池无法接受新的任务时,如何处理这些被拒绝的任务。在Spring框架中,可以通过配置ThreadPoolTaskExecutor来设置线程池的拒绝策略。

ThreadPoolTaskExecutor提供了几种常见的拒绝策略:

  • AbortPolicy(默认):当线程池无法接受新的任务时,直接抛出RejectedExecutionException异常。

  • CallerRunsPolicy:当线程池无法接受新的任务时,将任务返回给调用者执行。也就是说,如果线程池满了,任务会在调用者的线程中执行。

  • DiscardPolicy:当线程池无法接受新的任务时,直接丢弃这个任务,不做任何处理。

  • DiscardOldestPolicy:当线程池无法接受新的任务时,先丢弃最早加入队列的任务,然后尝试再次提交新的任务。

来,我们在代码中进行配置:

ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// AbortPolicy策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());// DiscardPolicy策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());// DiscardOldestPolicy策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());// CallerRunsPolicy策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

Lamba表达式的线程策略配置:

executor.setRejectedExecutionHandler((r, executor1) -> {// 拒绝策略的逻辑
});

总的来说,当异步任务的提交速度超过处理速度、资源有限或任务处理能力不足时,使用@Async的线程拒绝策略可以帮助你控制任务的提交速率,避免任务堆积和系统负载过高。这样可以提高系统的稳定性和可靠性,确保异步任务的顺利执行。

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

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

相关文章

如何根据接口文档,轻松快速的模拟接口服务?

什么是WireMock? WireMock 是一个Http 模拟服务,其核心也是一个web服务,WireMock主要是为特定请求提供固定的返回值。 WireMock可以作为单独进程启动,模拟一个WEB服务器,提供一些API访问,并返回特定的返回值。也可以作为第三方库在项目中使用。 如何使用 standalone方…

PTA:计算m到n之间所有素数的和

题目 计算m到n之间所有素数的和&#xff0c;其中 2 < m <n <100 输入格式: 请在这里写输入格式。例如&#xff1a;输入两个正整数 输出格式: 请在这里描述输出格式。例如&#xff1a;输出两个正整数之间的素数和。 样例 输入样例: 在这里给出一组输入。例如&…

2161根据数字划分数组

给你一个下标从 0 开始的整数数组 nums 和一个整数 pivot 。请你将 nums 重新排列&#xff0c;使得以下条件均成立&#xff1a; 所有小于 pivot 的元素都出现在所有大于 pivot 的元素 之前 。所有等于 pivot 的元素都出现在小于和大于 pivot 的元素 中间 。小于 pivot 的元素之…

C 语言预处理器

C 语言预处理器 在本教程中&#xff0c;将向您介绍c预处理器&#xff0c;并在示例的帮助下学习使用&#xff03;include&#xff0c;&#xff03;define和条件编译。C预处理程序是一个宏预处理程序&#xff08;允许您定义宏&#xff09;&#xff0c;可以在编译程序之前对其进行…

vue3如何判断是不是响应式数据

isRef: 检查一个值是否为一个 ref 对象 isReactive: 检查一个对象是否是由 reactive 创建的响应式代理 isReadonly: 检查一个对象是否是由 readonly 创建的只读代理 isProxy: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理 <template><div>&l…

allure修改logo 自定义

无论pytest还是httprunner都适用allure生成报告。那我们就有必要对allure报告进行一些定制。我们先修改logo&#xff1a; 1、给allure.yml插件custom-logo-plugin 找到allure安装的位置&#xff0c;在config文件夹下有一个allure.yml的配置文件。打开它&#xff0c;在最后添加…

Python接口自动化测试 ---Allure报告使用详解

这一节主要是记录allure的内容以及用法&#xff0c;怎么让他生成一个完整的想要的报告。 allure生成的报告和其他五花八门的报告对比了一下&#xff0c;它的可读性是最好、最直观的。这不仅仅是我想要的效果&#xff0c;也是很多小伙伴想要的结果&#xff0c;毕竟这是给领导看…

扩散模型DDPM学习笔记

扩散模型DDPM 文章目录 扩散模型DDPM如何运作基本概念训练过程推理过程&#xff1a; 目标损失函数推导评估标准 论文地址&#xff1a; Denoising Diffusion Probabilistic Models (DDPM) 如何运作 ​ 从guassian distribution进行采样得到一个噪声的图片&#xff0c;图片大小…

【复杂网络建模】——ER网络度分布、无标度网络度分布

目录 一、复杂网络介绍 二、ER网络、SF网络介绍 1、ER网络(Erdős-Rnyi网络)

测开笔记--Typescript: 文件复制到指定目录

开发背景&#xff1a; 自动化开发语言使用的是TypeScript&#xff1b;框架用的是playwright。有个测试脚本需要先将几个文件复制粘贴到新建的项目文件夹下&#xff0c;系统会读取该文件&#xff0c;然后生成页面信息。 关键字&#xff1a;文件复制粘贴&#xff1b; 新建的项目…

为什么我不能给shopify的图片添加alt

首先我们要明白是什么ALT标签&#xff0c;为什么要添加这个标签&#xff0c;这个标签有什么用 ALT标签是什么 ALT属性是HTML的一部分&#xff0c;它为那些无法查看图像的用户提供替代的文本描述。 ALT标签有什么用 使用ALT属性还可以帮助搜索引擎爬虫更好地理解您的网站内容。有…

SpringBoot配置多个不同Thymeleaf模板位置

最近开发当中需要在SpringBoot配置多个不同Thymeleaf位置&#xff0c;特此记录下相关过程 默认Thymeleaf配置 当我们集成thymeleaf后&#xff0c;会有一个默认的配置信息,可以在配置文件当中配置默认的信息&#xff0c;修改路径&#xff0c;前后缀等等参数 spring:thymeleaf…

华为OD机试 - 小华地图寻宝(Java JS Python C)

题目描述 小华按照地图去寻宝,地图上被划分成 m 行和 n 列的方格,横纵坐标范围分别是 [0, n-1] 和 [0, m-1]。 在横坐标和纵坐标的数位之和不大于 k 的方格中存在黄金(每个方格中仅存在一克黄金),但横坐标和纵坐标之和大于 k 的方格存在危险不可进入。小华从入口 (0,0) …

学习知识回顾随笔(远程连接MySQL|远程访问Django|HTTP协议|Web框架)

文章目录 如何远程连接MySQL数据库1.创建用户来运行&#xff0c;此用户从任何主机连接到mysql数据库2.使用IP地址来访问MySQL数据库 如何远程访问Django项目Web应用什么是Web应用应用程序的两种模式Web应用程序的优缺点 HTTP协议&#xff08;超文本传输协议&#xff09;简介HTT…

Airtest自动化测试工具实战演练

一开始知道Airtest大概是在年初的时候&#xff0c;当时&#xff0c;看了一下官方的文档&#xff0c;大概是类似Sikuli的一个工具&#xff0c;主要用来做游戏自动化的&#xff0c;通过截图的方式用来解决游戏自动化测试的难题。最近&#xff0c;移动端测试的同事尝试用它的poco库…

LCM-LoRA模型推理简明教程

潜在一致性模型 (LCM) 通常可以通过 2-4 个步骤生成高质量图像&#xff0c;从而可以在几乎实时的设置中使用扩散模型。 来自官方网站&#xff1a; LCM 只需 4,000 个训练步骤&#xff08;约 32 个 A100 GPU 小时&#xff09;即可从任何预训练的稳定扩散 (SD) 中提取出来&#…

sed文本 免交互

目录 什么是sed 概念 格式 基本用法 命令的选项 打印第三行 打印日志文件 打印奇数行 打印偶数行 第三行退出 删除第三行 sed在不打开文件的情况下修改文件内容 在后面添加 选项a 在字符中间添加 \n 实现追加换行 全部追加 在前面插入 选项i 替换 选项c …

HIVE SQL建表常用数据类型一览

类型描述BOOLEANtrue/falseTINYINTtrue/1字节的有符号整数 -128~127SMALLINT2个字节的有符号整数&#xff0c;-32768~32767INT4个字节的带符号整数BIGINT8字节带符号整数FLOAT4字节单精度浮点数DOUBLE8字节双精度浮点数DEICIMAL任意精度的带符号小数STRING字符串&#xff0c;变…

第二题-差值-【第六届传智杯程序设计挑战赛解题分析详解复盘】(JavaPythonC++实现)

🚀 欢迎来到 ACM 算法题库专栏 🚀 在ACM算法题库专栏,热情推崇算法之美,精心整理了各类比赛题目的详细解法,包括但不限于ICPC、CCPC、蓝桥杯、LeetCode周赛、传智杯等等。无论您是刚刚踏入算法领域,还是经验丰富的竞赛选手,这里都是提升技能和知识的理想之地。 ✨ 经典…

Java精品项目源码基于SpringBoot的智慧园区管理系统(v67)

Java精品项目源码基于SpringBoot的智慧园区管理系统(v67) 大家好&#xff0c;小辰今天给大家介绍一个智慧园区管理系统&#xff0c;演示视频公众号&#xff08;小辰哥的Java&#xff09;对号查询观看即可 文章目录 Java精品项目源码基于SpringBoot的智慧园区管理系统(v67)难度…