最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深度解析Spring Boot @Async 正在榨干服務(wù)器資源

 更新時(shí)間:2026年02月27日 15:15:30   作者:Omari7  
這篇文章主要介紹了Spring Boot @Async 正在榨干服務(wù)器資源,我們就來徹底搞懂 @Async 背后的線程池,并學(xué)會(huì)如何像個(gè)老手一樣駕馭它,讓你的應(yīng)用在高并發(fā)下穩(wěn)如泰山,需要的朋友可以參考下

“服務(wù)又掛了?日志里全是 OutOfMemoryError: unable to create new native thread!”

你是否也曾深夜被這樣的報(bào)警驚醒?明明只是一個(gè)簡單的異步處理,用 @Async 注解輕輕一標(biāo),本地測試跑得飛快,一到高并發(fā)壓測就瞬間“猝死”。這幾乎是每個(gè)從初級走向中級的 Java 開發(fā)者都會(huì)踩的坑。

問題出在哪里?出在你以為 @Async 是“銀彈”,但實(shí)際上,你只是把它當(dāng)成了一把沒加防護(hù)的“電鋸”在用。今天,我們就來徹底搞懂 @Async 背后的線程池,并學(xué)會(huì)如何像個(gè)老手一樣駕馭它,讓你的應(yīng)用在高并發(fā)下穩(wěn)如泰山。

核心概念拆解:把線程池當(dāng)成一個(gè)“智慧廚房”

忘掉那些枯燥的 API 定義,我們用一個(gè)更形象的方式來理解線程池的核心思想。

想象一下,你開了一家生意火爆的餐廳。

  • 請求 (Request):源源不斷進(jìn)店點(diǎn)餐的顧客。
  • 線程 (Thread):負(fù)責(zé)炒菜的廚師。

方案一:裸奔模式 (不使用線程池) 每來一位顧客,你就臨時(shí)去人才市場招一位新廚師。顧客少的時(shí)候還行,一旦到了飯點(diǎn)高峰期,幾百個(gè)顧客同時(shí)涌入,你的小廚房瞬間塞滿了臨時(shí)廚師,亂作一團(tuán),最終廚房(服務(wù)器內(nèi)存)被擠爆,餐廳(服務(wù))直接關(guān)門大吉。這就是 unable to create new native thread 的本質(zhì)——系統(tǒng)資源被無限創(chuàng)建的線程耗盡了。

方案二:智慧廚房模式 (使用線程池) 你學(xué)聰明了,建立了一個(gè)專業(yè)的廚師團(tuán)隊(duì)(線程池)。

  1. 核心廚師 (corePoolSize):你有 5 位正式廚師,他們是廚房的中堅(jiān)力量,時(shí)刻準(zhǔn)備接單。
  2. 等餐區(qū) (workQueue):當(dāng) 5 位廚師都在忙時(shí),新來的訂單會(huì)先放到一個(gè)等餐區(qū)(任務(wù)隊(duì)列)排隊(duì)。
  3. 臨時(shí)工 (maximumPoolSize):如果等餐區(qū)的訂單也排滿了(比如超過 100 單),說明生意實(shí)在太火爆了。你決定臨時(shí)再請 3 位廚師來幫忙,這樣廚房最多就有 8 位廚師。
  4. 拒絕策略 (RejectedExecutionHandler):如果 8 位廚師全在忙,等餐區(qū)也滿了,再有新顧客來,服務(wù)員(拒絕策略)就會(huì)禮貌地告訴他:“抱歉,今天太忙了,您稍后再來吧。” 而不是讓顧客無止境地等下去,最終導(dǎo)致體驗(yàn)崩潰。
  5. 臨時(shí)工的補(bǔ)貼 (keepAliveTime):高峰期過后,那 3 位臨時(shí)工如果連續(xù) 1 分鐘都沒接到新活,你就會(huì)讓他們下班,以節(jié)省成本(釋放空閑線程資源),只保留 5 位核心廚師。

看到了嗎?一個(gè)設(shè)計(jì)良好的線程池,就像一個(gè)管理有序的智慧廚房,它通過復(fù)用、排隊(duì)限流,確保了在任何客流量下,廚房都能高效、穩(wěn)定地運(yùn)轉(zhuǎn),而不是無限擴(kuò)張導(dǎo)致崩潰。

實(shí)戰(zhàn)落地:在 Spring Boot 中正確“馴服”@Async

光說不練假把式。我們來看如何在 Spring Boot 項(xiàng)目中,從“裸奔”模式進(jìn)化到“精細(xì)化”管理模式。

1. 依賴(Maven)

你只需要 Spring Boot 的 Web 啟動(dòng)器,@Async 的支持是內(nèi)置的。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.7.5</version> <!-- 請根據(jù)你的項(xiàng)目選擇合適的版本 -->
</dependency>

2. 啟用異步功能

在你的主啟動(dòng)類上添加 @EnableAsync 注解。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync // 開啟異步任務(wù)支持
public class HighConcurrencyApplication {
    public static void main(String[] args) {
        SpringApplication.run(HighConcurrencyApplication.class, args);
    }
}

3. 錯(cuò)誤的用法(裸奔的@Async)

很多新手會(huì)直接在一個(gè) Service 方法上標(biāo)注 @Async,就像這樣:

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
    @Async // 警告:這是默認(rèn)配置,高并發(fā)下極度危險(xiǎn)!
    public void processOrder(String orderId) {
        // 模擬處理訂單的耗時(shí)操作
        System.out.println(Thread.currentThread().getName() + " 開始處理訂單: " + orderId);
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " 訂單處理完成: " + orderId);
    }
}

為什么危險(xiǎn)? 因?yàn)樵跊]有自定義線程池的情況下,Spring Boot 會(huì)使用一個(gè) SimpleAsyncTaskExecutor。這個(gè)執(zhí)行器不會(huì)復(fù)用線程,每次調(diào)用都會(huì)創(chuàng)建一個(gè)新線程。這和我們“智慧廚房”的反面教材——方案一,一模一樣。

4. 正確的姿勢:自定義線程池

我們需要?jiǎng)?chuàng)建一個(gè)配置類,來定義我們自己的“智慧廚房”。

ThreadPoolConfig.java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
public class ThreadPoolConfig {
    // Bean 的名稱,我們將在 @Async 注解中引用它
    public static final String ASYNC_EXECUTOR_NAME = "myAsyncExecutor";
    @Bean(name = ASYNC_EXECUTOR_NAME)
    public Executor myAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心廚師數(shù)量:根據(jù)CPU核心數(shù)動(dòng)態(tài)計(jì)算
        int corePoolSize = Runtime.getRuntime().availableProcessors();
        executor.setCorePoolSize(corePoolSize); 
        // 最大廚師數(shù)量:通常是核心數(shù)的2-3倍
        executor.setMaxPoolSize(corePoolSize * 2);
        // 等餐區(qū)容量
        executor.setQueueCapacity(100); 
        // 臨時(shí)工的存活時(shí)間
        executor.setKeepAliveSeconds(60); 
        // 給廚師起個(gè)好名字,方便排查問題
        executor.setThreadNamePrefix("My-Async-"); 
        // 拒絕策略:讓調(diào)用者自己處理,這是最穩(wěn)妥的方式
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 初始化
        executor.initialize();
        return executor;
    }
}

application.yml (推薦)

為了靈活性,最好將核心參數(shù)配置在 application.yml 中。

myapp:
  async:
    core-pool-size: 8
    max-pool-size: 16
    queue-capacity: 200
    keep-alive-seconds: 60

然后在配置類中通過 @Value 注入這些值。

5. 在業(yè)務(wù)代碼中指定線程池

現(xiàn)在,讓我們的 OrderService 使用新定義的“智慧廚房”。

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import com.yourpackage.config.ThreadPoolConfig; // 引入你的配置類
@Service
public class OrderService {
    // 通過 value 屬性指定要使用的線程池 Bean 名稱
    @Async(ThreadPoolConfig.ASYNC_EXECUTOR_NAME)
    public void processOrder(String orderId) {
        // ... 業(yè)務(wù)邏輯不變 ...
        System.out.println("使用自定義線程池 [" + Thread.currentThread().getName() + "] 開始處理訂單: " + orderId);
        // ...
    }
}

現(xiàn)在,所有的異步訂單處理任務(wù)都會(huì)被提交到我們精心設(shè)計(jì)的 myAsyncExecutor 線程池中,再也不怕野線程滿天飛了。

避坑指南:生產(chǎn)環(huán)境的“秘密”

  • 小心“無底洞”隊(duì)列:千萬不要將 queueCapacity 設(shè)置得過大,或者使用 Executors.newFixedThreadPool(),它內(nèi)部使用的是無界的 LinkedBlockingQueue。在高并發(fā)下,如果任務(wù)處理速度跟不上生產(chǎn)速度,會(huì)導(dǎo)致請求在隊(duì)列中大量堆積,最終引發(fā) OOM。有限的隊(duì)列是保護(hù)系統(tǒng)的第一道防線。
  • 優(yōu)雅停機(jī)很重要:Spring Boot 默認(rèn)會(huì)等待異步任務(wù)執(zhí)行完畢再關(guān)閉應(yīng)用。但你需要通過 spring.task.execution.shutdown.await-termination=truespring.task.execution.shutdown.await-termination-period 來確保有足夠的時(shí)間讓任務(wù)完成,防止服務(wù)關(guān)閉時(shí)丟失正在處理的數(shù)據(jù)。
  • 別忘了線程上下文:如果你在異步方法中需要獲取 RequestContext(如用戶信息、TraceID),默認(rèn)情況下是拿不到的。你需要自己處理上下文的傳遞,或者使用像 TransmittableThreadLocal 這樣的庫來自動(dòng)完成。這又是另一個(gè)深坑,值得單獨(dú)寫一篇文章。

總結(jié)與升華

我們今天從一個(gè)常見的 OOM 慘案出發(fā),通過一個(gè)“智慧廚房”的比喻,徹底理解了線程池的工作原理。核心的收獲是:

永遠(yuǎn)不要在生產(chǎn)環(huán)境中使用默認(rèn)的 @Async,必須為其提供一個(gè)經(jīng)過精細(xì)化配置的自定義線程池。

這不僅是代碼技巧,更是一種對系統(tǒng)負(fù)責(zé)的架構(gòu)思維。我們從“能用”走向了“可靠、可控”。

最后,留一個(gè)思考題:我們今天討論的都是單個(gè)應(yīng)用內(nèi)的線程池。如果我們的系統(tǒng)是分布式的微服務(wù)架構(gòu),一個(gè)請求需要跨越多個(gè)服務(wù),我們又該如何控制整個(gè)鏈路的并發(fā)和資源,防止上游的流量洪峰打垮下游所有服務(wù)呢?

到此這篇關(guān)于Spring Boot @Async 正在榨干服務(wù)器資源的文章就介紹到這了,更多相關(guān)Spring Boot @Async服務(wù)器資源內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

麻栗坡县| 思南县| 东方市| 蓝田县| 霍林郭勒市| 城市| 蒲城县| 麻江县| 盐城市| 肇州县| 永平县| 静安区| 铅山县| 琼中| 会同县| 宜宾县| 车致| 唐山市| 延边| 哈尔滨市| 铁岭市| 彭山县| 涿鹿县| 泰兴市| 焦作市| 额尔古纳市| 广饶县| 山东| 新竹市| 务川| 宜章县| 古丈县| 东乡县| 连江县| 安图县| 南江县| 平阴县| 南部县| 华亭县| 汝南县| 云阳县|