欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java Spring的@Async的使用及注意事項示例總結

 更新時間:2025年05月13日 10:28:57   作者:六千江山  
這篇文章主要介紹了Java Spring的@Async使用及注意事項的相關資料,Spring框架中的@Async注解用于標記方法在單獨線程中異步執(zhí)行,適用于耗時操作,提高性能和響應速度,需要的朋友可以參考下

1、概念和用途

@Async是 Spring 框架提供的一個注解,用于標記一個方法,在一個單獨的線程中異步執(zhí)行。

這在處理一些耗時的操作(比如發(fā)送郵件、調用外部 API 等)時非常有用。

通過使用@Async,可以讓這些操作在后臺執(zhí)行,而不會阻塞主線程,從而提高應用程序的性能和響應速度。

例如,在一個Web應用程序中,當用戶提交一個訂單后,可能需要發(fā)送一封確認郵件。如果使用同步方式,用戶必須等待郵件發(fā)送完成后才能得到訂單提交成功的響應。而使用@Async,可以讓郵件發(fā)送操作在后臺線程中進行,用戶幾乎可以立即得到訂單提交成功的響應。

2、使用

2.1 啟用異步支持

首先,需要在 Spring 配置類上添加@EnableAsync注解來開啟異步方法執(zhí)行功能。這個注解會掃描帶有@Async標記的方法,并為它們創(chuàng)建獨立的線程來執(zhí)行。

示例配置類如下:

@Configuration
@EnableAsync
public class AppConfig {
    // 可以在這里進行其他配置,如Bean定義等
}

2.2 標記異步方法

在需要異步執(zhí)行的方法上添加@Async注解。這個方法通常應該返回void或者Future類型。

如果返回void,方法執(zhí)行完成后不會返回任何結果。如果返回Future,可以在之后獲取異步方法的執(zhí)行結果。

例如,下面是一個簡單的異步方法,它模擬了一個耗時的操作:

@Service
public class MyService {
    @Async
    public void doSomethingAsync() {
        try {
            // 模擬耗時操作,這里休眠3秒
            Thread.sleep(30000);
            System.out.println("異步方法執(zhí)行完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

2.3 調用異步方法

可以在其他組件(如控制器、其他服務方法等)中調用這個異步方法。調用時,方法會立即返回,而實際的操作會在后臺線程中執(zhí)行。

例如,在一個 Spring MVC 控制器中調用上述異步方法:

@RestController
public class MyController {
    @Autowired
    private MyService myService;

    @GetMapping("/async")
    public String asyncEndpoint() {
        myService.doSomethingAsync();
        return "異步操作已啟動";
    }
}

2.4 需要異步結果時

如果異步方法需要返回一個結果,可以將方法的返回類型定義為Future。Future接口是 Java 并發(fā)包中的一部分,用于表示一個異步計算的結果。

例如,修改前面的MyService中的方法如下:

@Async
public Future<String> doSomethingAsyncWithResult() {
    try {
        // 模擬耗時操作,這里休眠3秒
        Thread.sleep(3000);
        return new AsyncResult<>("異步方法執(zhí)行結果");
    } catch (InterruptedException e) {
        e.printStackTrace();
        return null;
    }
}

然后在調用這個方法的地方,可以通過Future的get方法來獲取結果:

@GetMapping("/async - result")
public String asyncResultEndpoint() {
    try {
        Future<String> futureResult = myService.doSomethingAsyncWithResult();
        String result = futureResult.get();
        return result;
    } catch (Exception e) {
        e.printStackTrace();
        return "獲取結果出錯";
    }
}

注意:get方法會阻塞當前線程,直到異步方法執(zhí)行完成并返回結果

3、注意事項

3.1 線程池

默認情況下,Spring 使用SimpleAsyncTaskExecutor來執(zhí)行異步任務,這個執(zhí)行器會為每個任務創(chuàng)建一個新的線程。在高并發(fā)場景下,這可能導致系統資源耗盡,因為創(chuàng)建線程是一個比較耗費資源的操作。而且過多的線程會增加上下文切換的成本,降低系統的整體性能。

例如,假設有一個 Web 應用,大量用戶同時觸發(fā)帶有@Async注解的方法,如果不配置線程池,可能會創(chuàng)建大量線程,使服務器的 CPU 和內存資源被大量占用,最終導致應用程序響應緩慢甚至崩潰。

可以通過配置自定義的線程池來優(yōu)化。

例如,定義一個線程池配置類:

@Configuration
public class ThreadPoolConfig {
    @Bean("asyncExecutor")
    public Executor asyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(25);
        executor.setThreadNamePrefix("Async - ");
        executor.initialize();
        return executor;
    }
}

然后在@Async注解中指定線程池名稱:

@Async("asyncExecutor")
public void doSomethingAsync() {
    // 方法內容
}

合理設置線程池參數

  • 核心線程數(CorePoolSize):這是線程池一直保持的線程數量,即使線程處于空閑狀態(tài)也不會被銷毀。應該根據應用程序的平均負載來設置。例如,如果應用程序通常需要同時處理 5 個異步任務,那么可以將核心線程數設置為 5。
  • 最大線程數(MaxPoolSize):它定義了線程池允許創(chuàng)建的最大線程數量。當任務隊列已滿且有新任務到來時,線程池會創(chuàng)建新線程,直到達到最大線程數。設置時要考慮系統資源限制和任務的突發(fā)情況。如果系統資源有限,不能無限制地增加線程數。
  • 任務隊列容量(QueueCapacity):用于存儲等待執(zhí)行的任務。當線程池中的線程都在忙碌時,新任務會被放入任務隊列。如果隊列已滿,且未達到最大線程數,才會創(chuàng)建新線程。隊列容量的大小應該根據任務的平均處理時間和任務的產生頻率來確定。

線程池的復用和管理

  • 配置好的線程池可以復用線程,提高線程的利用率。通過合理設置線程池的參數,可以使線程在任務之間高效切換,減少線程創(chuàng)建和銷毀的開銷。同時,需要注意線程池的生命周期管理,在應用程序關閉時,應該正確地關閉線程池,以避免資源泄漏。

3.2 異常處理 

異常不會自動傳播給調用者,這是使用@Async時一個容易被忽視的問題。

當異步方法拋出異常時,異常不會像同步方法那樣直接傳播到調用者。這是因為異步方法在另一個線程中執(zhí)行,異常在這個線程中被拋出,如果不進行特殊處理,調用者可能完全不知道異步方法出現了問題。

例如,在一個業(yè)務邏輯中,調用了一個帶有@Async注解的方法來更新數據庫記錄,若該異步方法在執(zhí)行過程中拋出了SQLException,如果沒有處理這個異常,調用者可能會繼續(xù)執(zhí)行后續(xù)的操作,認為更新操作已經成功,從而導致數據不一致等問題。

在異步方法內部處理異常,可以在異步方法內部使用try - catch塊來捕獲和處理異常。這樣可以在異步方法內部對異常進行記錄、重試或者進行一些補救措施。

例如:

@Async
public void asyncMethod() {
    try {
        // 可能會拋出異常的代碼
    } catch (Exception e) {
        // 記錄異常日志
        logger.error("異步方法出現異常", e);
        // 可以在這里進行重試或者其他補救措施
    }
}

配置全局異步異常處理機制,如果不想在每個異步方法內部都處理異常,可以實現AsyncUncaughtExceptionHandler接口來配置全局的異步異常處理機制。這個接口有一個handleUncaughtException方法,當異步方法拋出未捕獲的異常時會被調用。

例如:

@Configuration
@EnableAsync
public class AppConfig implements AsyncUncaughtExceptionHandler {
    // 開啟異步支持的配置
    @Override
    public void handleUncaughtException(Throwable ex, Method method, Object... params) {
        // 記錄異常日志
        logger.error("異步方法出現未捕獲異常,方法名: " + method.getName(), ex);
        // 可以在這里進行全局的異常處理策略,如通知管理員等
    }
}

3.3 同類中調用異步方法

如果在一個類中,一個方法(方法 A)調用了同一個類中的另一個帶有@Async注解的方法(方法 B),默認情況下@Async注解可能不會生效。

這是因為 Spring 的代理機制導致的,方法 A 直接調用方法 B 時,實際上沒有通過代理對象來調用,所以不會觸發(fā)異步執(zhí)行。

例如,在一個Service類中:

@Service
public class MyService {
    @Async
    public void asyncMethod() {
        // 異步執(zhí)行的代碼
    }
    public void anotherMethod() {
        asyncMethod(); // 這種情況下,@Async可能不會生效
    }
}

解決方法是將方法 B 的調用通過注入的代理對象來進行??梢酝ㄟ^@Autowired將當前類自己注入進來,然后通過代理對象調用方法 B。

例如:

@Service
public class MyService {
    @Autowired
    private MyService self;
    @Async
    public void asyncMethod() {
        // 異步執(zhí)行的代碼
    }
    public void anotherMethod() {
        self.asyncMethod(); // 通過代理對象調用,@Async生效
    }
}

3.4 循環(huán)依賴

在使用@Async時,如果涉及到循環(huán)依賴,可能會導致應用程序啟動失敗或者出現異常行為。因為異步方法的代理對象創(chuàng)建和循環(huán)依賴的解決可能會相互沖突。

例如,有兩個服務類ServiceA和ServiceB,它們相互依賴并且都有@Async注解的方法。

在這種情況下,需要仔細檢查依賴注入的方式和異步方法的使用,避免出現循環(huán)依賴導致的問題。可以通過調整依賴注入的順序、使用@Lazy注解等方式來緩解循環(huán)依賴問題。

總結

到此這篇關于Java Spring的@Async的使用及注意事項的文章就介紹到這了,更多相關Spring的@Async使用及注意內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java學習隨記之多線程編程

    Java學習隨記之多線程編程

    這篇文章主要介紹了Java中的多線程編程的相關知識,文中的示例代碼介紹詳細,對我們的學習或工作有一定的價值,感興趣的小伙伴可以了解一下
    2021-12-12
  • Java 讀取PDF中的文本和圖片的方法

    Java 讀取PDF中的文本和圖片的方法

    本文將介紹通過Java程序來讀取PDF文檔中的文本和圖片的方法。分別調用方法extractText()和extractImages()來讀取,需要的朋友可以參考下
    2019-07-07
  • SpringBoot之Order注解啟動順序說明

    SpringBoot之Order注解啟動順序說明

    這篇文章主要介紹了SpringBoot之Order注解啟動順序說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Javafx利用fxml變換場景的實現示例

    Javafx利用fxml變換場景的實現示例

    本文主要介紹了Javafx利用fxml變換場景的實現示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-07-07
  • java實現的DES加密算法詳解

    java實現的DES加密算法詳解

    這篇文章主要介紹了java實現的DES加密算法,結合實例形式詳細分析了java實現DES加密操作的原理、實現技巧與相關注意事項,需要的朋友可以參考下
    2017-06-06
  • Java中的異常和處理機制實例詳解

    Java中的異常和處理機制實例詳解

    這篇文章主要介紹了Java中的異常和處理機制,結合實例形式詳細分析了Java異常與處理機制的相關概念、原理、用法及操作注意事項,需要的朋友可以參考下
    2019-05-05
  • springboot項目中jacoco服務端部署使用

    springboot項目中jacoco服務端部署使用

    這篇文章主要為大家介紹了springboot項目中jacoco服務端部署使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • 基于XML配置Spring的自動裝配過程解析

    基于XML配置Spring的自動裝配過程解析

    這篇文章主要介紹了基于XML配置Spring的自動裝配過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-10-10
  • 淺析Java8新特性Lambda表達式和函數式接口

    淺析Java8新特性Lambda表達式和函數式接口

    Lambda表達式理解為是 一段可以傳遞的代碼。最直觀的是使用Lambda表達式之后不用再寫大量的匿名內部類,簡化代碼,提高了代碼的可讀性
    2017-08-08
  • java Disruptor構建高性能內存隊列使用詳解

    java Disruptor構建高性能內存隊列使用詳解

    這篇文章主要為大家介紹了java Disruptor構建高性能內存隊列使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12

最新評論