Springboot 異步場景 使用注解 @Async 及 自定義線程池分模塊使用

目錄

  • 前言
  • 一、Springboot項目如何開啟異步?
  • 二、存在的問題
  • 三、自定義線程池
  • 四、自定義線程池使用
  • 五、阻塞隊列和拒絕策略


前言

當開發中遇到不影響主流程任務時,使用異步去處理。
如有以下場景:
1、業務需要生成一個季度的數據進行員工排名(涉及到的數據很多),數據查詢、組裝、按規則排名耗時比較長,并且開發方案能接受延時查看具體排名信息數據。在數據變動時,需要調用重新排名的方法,故把排名方法設置為異步。


一、Springboot項目如何開啟異步?

啟動類 上添加或者 自定義線程池 上添加注解:@EnableAsync。
執行方法上加上注解 @Async。

啟動類配置
在這里插入圖片描述
Controller
在這里插入圖片描述

Service
在這里插入圖片描述
打印信息
在這里插入圖片描述
到這里就可以正常使用異步了。

二、存在的問題

雖然在 Spring 框架中,@Async 注解可以用于異步執行方法。

但是Spring 會自動創建一個默認的線程池用于執行方法。這個默認線程池是由 SimpleAsyncTaskExecutor 管理的,它為每個任務創建一個新的線程。雖然這可以工作,但可能會遇到以下問題:

  1. 無限制的線程創建:SimpleAsyncTaskExecutor 會為每個任務創建一個新的線程,而沒有最大線程數的限制。如果異步任務的數量非常多,這可能導致大量的線程被創建,消耗大量的系統資源,最終可能導致 OutOfMemoryError 或降低系統性能。
  2. 線程管理:由于每次調用都會創建新線程,沒有線程復用,這可能會導致線程管理上的開銷,尤其是在高并發場景下。
  3. 調試和監控困難:默認線程池創建的線程名稱沒有明確的命名規則,這可能會使得在日志中或監控工具中跟蹤異步任務變得困難。
  4. 資源競爭:大量的線程可能會引起CPU和內存資源的激烈競爭,尤其是在JVM和操作系統層面上的上下文切換。
  5. 安全性問題:如果異步任務執行的時間過長,而默認線程池沒有適當的管理策略,可能會因為線程過多而影響到系統的穩定性和安全性。

三、自定義線程池

鑒于以上問題,建議使用自定義線程池。

import cn.hutool.core.thread.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;/*** 全局異步任務配置類*/
@Slf4j
@EnableAsync
@Configuration
public class GlobalAsyncConfig implements AsyncConfigurer {// 從 application.yml 中注入配置參數@Value("${async.executor.core-pool-size:10}")private int corePoolSize;@Value("${async.executor.max-pool-size:50}")private int maxPoolSize;@Value("${async.executor.keep-alive-seconds:60}")private int keepAliveSeconds;@Value("${async.executor.queue-capacity:200}")private int queueCapacity;/*** 創建主異步線程池** @return Executor*/@Bean(name = "asyncExecutor")public Executor asyncExecutor() {return createThreadPool("async-pool-", "MainAsyncTask");}/*** 創建郵件發送專用線程池(可選擴展)** @return Executor*/@Bean(name = "emailExecutor")public Executor emailExecutor() {return createThreadPool("email-pool-", "EmailTask");}/*** 創建線程池通用方法** @param namePrefix 線程名稱前綴* @param taskType   任務類型描述(用于日志區分)* @return ThreadPoolTaskExecutor*/private Executor createThreadPool(String namePrefix, String taskType) {// 使用 Spring 提供的 ThreadPoolTaskExecutor,相較于原生 ThreadPoolExecutor,// 更加適合與 Spring 的 @Async 注解配合使用,并且支持更豐富的配置選項。ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 設置核心線程數,線程池初始化時創建的線程數量executor.setCorePoolSize(corePoolSize);// 設置最大線程數,當任務隊列滿時,線程池最多可擴容到的線程數量executor.setMaxPoolSize(maxPoolSize);// 設置非核心線程空閑存活時間(單位為秒)executor.setKeepAliveSeconds(keepAliveSeconds);// 設置任務隊列容量,用于緩存待執行的任務executor.setQueueCapacity(queueCapacity);// 設置線程工廠,用于創建具有指定命名前綴的線程,便于日志追蹤和問題定位executor.setThreadFactory(createThreadFactory(namePrefix));// 設置拒絕策略:當線程池和任務隊列都已滿時,由調用線程(即提交任務的線程)自己執行該任務executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());// 允許核心線程在空閑時超時并被回收,有助于節省資源(適用于負載波動較大的場景)executor.setAllowCoreThreadTimeOut(true);// 設置線程名稱前綴,方便在日志中識別不同線程池中的線程executor.setThreadNamePrefix("[" + taskType + "] ");// 必須顯式調用 initialize() 來啟動線程池executor.initialize();// 返回配置完成的線程池實例return executor;}/*** 創建線程工廠(統一命名格式)** @param prefix 線程名稱前綴* @return ThreadFactory*/private ThreadFactory createThreadFactory(String prefix) {return new ThreadFactoryBuilder().setNamePrefix(prefix).build();}@Overridepublic Executor getAsyncExecutor() {return asyncExecutor();}@Overridepublic AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {return new GlobalAsyncUncaughtExceptionHandler();}/*** 異步任務全局異常處理器,這里可以單獨放一個類文件(這里鑒于篇幅寫在一起)*/@Slf4jpublic static class GlobalAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {@Overridepublic void handleUncaughtException(Throwable ex, Method method, Object... params) {log.error("[Async Task Error] Method: {}, Params: {}", method.getName(), Arrays.deepToString(params), ex);}}
}

application.yml配置(已有默認值,看個人需求配置)

# 全局線程池相關配置
async:executor:core-pool-size: 10max-pool-size: 50keep-alive-seconds: 60queue-capacity: 200

四、自定義線程池使用

在業務開發中,建議每塊業務區分線程池使用,模塊互不影響,便于日志收集。

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

五、阻塞隊列和拒絕策略

Java 中常用的阻塞隊列(BlockingQueue)

隊列類型特點適用場景
ArrayBlockingQueue基于數組、有界、FIFO內存敏感、任務量可控的系統
LinkedBlockingQueue基于鏈表、可有界也可無界、FIFO通用型,吞吐量要求較高
PriorityBlockingQueue支持優先級排序、無界需要按優先級處理的任務(如報警、日志級別)
SynchronousQueue不存儲元素,插入必須等待取出高并發、低延遲場景,任務直接由消費者線程執行

🚫 線程池拒絕策略(RejectedExecutionHandler)

策略類名行為說明使用建議
AbortPolicy拋出異常 RejectedExecutionException默認策略,適用于不能丟失任務的場景
CallerRunsPolicy由調用線程自己執行任務減緩提交速度,適合臨時過載時
DiscardOldestPolicy丟棄隊列中最老的任務,嘗試重新提交當前任務可接受部分任務丟失,希望保留最新任務
DiscardPolicy默默丟棄任務,不做任何處理可容忍任務丟失,如非關鍵日志、監控等任務

在這里插入圖片描述
默認使用的是LinkedBlockingQueue隊列,建議初始化大小,防止內存溢出。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/80182.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/80182.shtml
英文地址,請注明出處:http://en.pswp.cn/web/80182.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【GNN筆記】Signed Graph Convolutional Network(12)【未完】

視頻鏈接:《圖神經網絡》 Signed Graph Convolutional Network 之前介紹的GNN模型主要集中在無符號的網絡(或僅由正鏈接組成的圖)上,符號 圖帶來的挑戰,主要集中在于 否定鏈接,與正鏈接相比,它不…

米勒電容補償的理解

米勒電容補償是使運放放大器穩定的重要手法,可以使兩級運放的兩個極點分離,從而可以得到更好的相位裕度。 Miller 電容補償的本質是增加一條通路流電流,流電流才是miller效應的本質。給定一個相同的輸入,Miller 電容吃掉的電流比…

CVE-2017-8046 漏洞深度分析

漏洞概述 CVE-2017-8046 是 Spring Data REST 框架中的一個高危遠程代碼執行漏洞&#xff0c;影響版本包括 Spring Data REST < 2.5.12、2.6.7、3.0 RC3 及關聯的 Spring Boot 和 Spring Data 舊版本。攻擊者通過構造包含惡意 SpEL&#xff08;Spring Expression Language&…

qt文本邊框設置

// 計算文本的大致尺寸 QFontMetrics fm(textEditor->font()); QRect textRect fm.boundingRect(textItem->toPlainText()); // 設置編輯框大小&#xff0c;增加一些邊距 const int margin 10; textEditor->setGeometry( center.x() - textRect.width()/2 - margin,…

Java 與 面向對象編程(OOP)

Java 是典型的純面向對象編程語言&#xff08;Pure Object-Oriented Language&#xff09;&#xff0c;其設計嚴格遵循面向對象&#xff08;OOP&#xff09;的核心原則。以下是具體分析&#xff1a; 1. Java 的面向對象核心特性 (1) 一切皆對象 Java 中幾乎所有的操作都圍繞…

導出導入Excel文件(詳解-基于EasyExcel)

前言&#xff1a; 近期由于工作的需要&#xff0c;根據需求需要導出導入Excel模板。于是自學了一下下&#xff0c;在此記錄并分享&#xff01;&#xff01; EasyExcel&#xff1a; 首先我要在這里非常感謝阿里的大佬們&#xff01;封裝這么好用的Excel相關的API&#xff0c;真…

python版本管理工具-pyenv輕松切換多個Python版本

在使用python環境開發時&#xff0c;相信肯定被使用版本所煩惱&#xff0c;在用第三方庫時依賴兼容的python版本不一樣&#xff0c;有沒有一個能同時安裝多個python并能自由切換的工具呢&#xff0c;那就是pyenv&#xff0c;讓你可以輕松切換多個Python 版本。 pyenv是什么 p…

Elasticsearch 索引副本數

作者&#xff1a;來自 Elastic Kofi Bartlett 解釋如何配置 number_of_replicas、它的影響以及最佳實踐。 更多閱讀&#xff1a;Elasticsearch 中的一些重要概念: cluster, node, index, document, shards 及 replica 想獲得 Elastic 認證&#xff1f;查看下一期 Elasticsearc…

AXI4總線協議 ------ AXI_LITE協議

一、AXI 相關知識介紹 https://download.csdn.net/download/mvpkuku/90841873 AXI_LITE 選出部分重點&#xff0c;詳細文檔見上面鏈接。 1.AXI4 協議類型 2.握手機制 二、AXI_LITE 協議的實現 1. AXI_LITE 通道及各通道端口功能介紹 2.實現思路及框架 2.1 總體框架 2.2 …

idea運行

各種小kips Linuxidea上傳 Linux 部署流程 1、先在idea打好jar包&#xff0c;clean之后install 2、在Linux目錄下&#xff0c;找到對應項目目錄&#xff0c;把原來的jar包放在bak文件夾里面 3、殺死上一次jar包的pid ps -ef|grep cliaidata.jar kill pid 4、再進行上傳新的jar…

FPGA: XILINX Kintex 7系列器件的架構

本文將詳細介紹Kintex-7系列FPGA器件的架構。以下內容將涵蓋Kintex-7的核心架構特性、主要組成部分以及關鍵技術&#xff0c;盡量全面且結構化&#xff0c;同時用簡潔的語言確保清晰易懂。 Kintex-7系列FPGA架構概述 Kintex-7是Xilinx 7系列FPGA中的中高端產品線&#xff0c;基…

【LLM】大模型落地應用的技術 ——— 推理訓練 MOE,AI搜索 RAG,AI Agent MCP

【LLM】大模型落地應用的技術 ——— 推理訓練MOE&#xff0c;AI搜索RAG&#xff0c;AI Agent MCP 文章目錄 1、推理訓練 MOE2、AI搜索 RAG3、AI Agent MCP 1、推理訓練 MOE MoE 是模型架構革新&#xff0c;解決了算力瓶頸。原理是多個專家模型聯合計算。 推理訓練MoE&#xff…

10 web 自動化之 yaml 數據/日志/截圖

文章目錄 一、yaml 數據獲取二、日志獲取三、截圖 一、yaml 數據獲取 需要安裝 PyYAML 庫 import yaml import os from TestPOM.common import dir_config as Dir import jsonpathclass Data:def __init__(self,keyNone,file_name"test_datas.yaml"):file_path os…

中exec()函數因$imagePath參數導致的命令注入漏洞

exec(zbarimg -q . $imagePath, $barcodeList, $returnVar); 針對PHP中exec()函數因$imagePath參數導致的命令注入漏洞&#xff0c;以下是安全解決方案和最佳實踐&#xff1a; 一、漏洞原理分析 直接拼接用戶輸入$imagePath到系統命令中&#xff0c;攻擊者可通過注入特殊字…

this.$set的用法-響應式數據更新

目錄 一、核心作用 三、使用場景與示例 1. 給對象添加新屬性 四、與 Vue.set 的關系 五、底層原理 六、Vue 3 的替代方案 七、最佳實踐 八、常見問題 Q&#xff1a;為什么修改嵌套對象屬性不需要 $set&#xff1f; Q&#xff1a;$set 和 $forceUpdate 的區別&#xf…

【生成式AI文本生成實戰】DeepSeek系列應用深度解析

目錄 &#x1f31f; 前言&#x1f3d7;? 技術背景與價值&#x1fa79; 當前技術痛點&#x1f6e0;? 解決方案概述&#x1f465; 目標讀者說明 &#x1f9e0; 一、技術原理剖析&#x1f4ca; 核心概念圖解&#x1f4a1; 核心作用講解&#x1f527; 關鍵技術模塊說明?? 技術選…

c/c++的opencv的圖像預處理講解

OpenCV 圖像預處理核心技術詳解 (C/C) 圖像預處理是計算機視覺任務中至關重要的一步。原始圖像往往受到噪聲、光照不均、尺寸不一等多種因素的影響&#xff0c;直接用于后續分析&#xff08;如特征提取、目標檢測、機器學習模型訓練等&#xff09;可能會導致性能下降或結果不準…

使用 Docker 部署 React + Nginx 應用教程

目錄 1. 創建react項目結構2. 創建 .dockerignore3. 創建 Dockerfile4. 創建 nginx.conf5. 構建和運行6. 常用命令 1. 創建react項目結構 2. 創建 .dockerignore # 依賴目錄 node_modules npm-debug.log# 構建輸出 dist build# 開發環境文件 .git .gitignore .env .env.local …

Java 流(Stream)API

一、理論說明 1. 流的定義 Java 流&#xff08;Stream&#xff09;是 Java 8 引入的新特性&#xff0c;用于對集合&#xff08;如 List、Set&#xff09;或數組進行高效的聚合操作&#xff08;如過濾、映射、排序&#xff09;和并行處理。流不存儲數據&#xff0c;而是按需計…

網絡協議分析 實驗七 FTP、HTTP、DHCP

文章目錄 實驗7.1 FTP協議練習二 使用瀏覽器登入FTP練習三 在窗口模式下&#xff0c;上傳/下傳數據文件實驗7.2 HTTP(Hyper Text Transfer Protocol)練習二 頁面提交練習三 訪問比較復雜的主頁實驗7.3 DHCP(Dynamic Host Configuration Protocol) 實驗7.1 FTP協議 dir LIST&…