Spring:@Async注解和AsyncResult與CompletableFuture使用問(wèn)題
@Async概述
Spring中用@Async注解標(biāo)記的方法,稱(chēng)為異步方法,它會(huì)在調(diào)用方的當(dāng)前線(xiàn)程之外的獨(dú)立的線(xiàn)程中執(zhí)行,
其實(shí)就相當(dāng)于我們自己
new Thread(()-> System.out.println("hello world !"))
這樣在另一個(gè)線(xiàn)程中去執(zhí)行相應(yīng)的業(yè)務(wù)邏輯
異步方法實(shí)際的執(zhí)行交給了 Spring 的 TaskExecutor 來(lái)完成。
@Async使用
- @Async注解如果用在類(lèi)的方法上,則說(shuō)明改方法是異步方法
- @Async注解如果用在類(lèi)上,那么這個(gè)類(lèi)所有的方法都是異步執(zhí)行的
- @Async注解方法的類(lèi)對(duì)象應(yīng)該是Spring容器管理的bean對(duì)象
- 調(diào)用異步方法類(lèi)上需要配置上注解@EnableAsync,或者是在啟動(dòng)類(lèi)上加上@EnableAsync
@Async注意
- 默認(rèn)情況下(@EnableAsync注解的
mode=AdviceMode.PROXY
),同一個(gè)類(lèi)內(nèi)部沒(méi)有使用@Async注解修飾的方法調(diào)用@Async注解修飾的方法,是不會(huì)異步執(zhí)行的 - 如果想實(shí)現(xiàn)類(lèi)內(nèi)部自調(diào)用也可以異步,則需要切換@EnableAsync注解的mode=
AdviceMode.ASPECTJ
- 任意參數(shù)類(lèi)型都是支持的,但是方法返回值必須是void或者Future類(lèi)型
@Async代碼示例
使用1代碼實(shí)現(xiàn):調(diào)用異步方法類(lèi)上配置@EnableAsync
import org.springframework.scheduling.annotation.Async; public interface TestService { @Async void test(); }
import com.example.service.TestService; import org.springframework.stereotype.Service; @Service public class TestServiceImpl implements TestService { @Override public void test() { //... } }
調(diào)用異步方法的controller類(lèi)
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping(value = "/test") @EnableAsync public class TestContoller { @Autowired private TestService testService; @GetMapping(value = "/testAsync") public void print() { testService.test(); } }
如果我們?nèi)コ齌estController上的@EnableAsync或者new 一個(gè)TestService對(duì)象(該對(duì)象沒(méi)有加載進(jìn)Spring的容器中),那么TestController中的print()方法都會(huì)同步執(zhí)行,后臺(tái)打印日志也可以看到只有一個(gè)線(xiàn)程在執(zhí)行
使用2代碼實(shí)現(xiàn):在啟動(dòng)類(lèi)上配置@EnableAsync
如果啟動(dòng)類(lèi)上配置@EnableAsync,那么在上例代碼中,便不需要再在Controller上面加上@EnableAsync這個(gè)注解
入口類(lèi)增加了 @EnableAsync 注解,主要是為了掃描范圍包下的所有 @Async 注解。
注意1代碼實(shí)現(xiàn):@Async沒(méi)有生效
@Async 修飾test()方法,test2()方法不修飾
public interface TestService { @Async void test(); void test2(); }
public class TestServiceImpl implements TestService{ @Override public void test() { //... } @Override public void test2() { test();//自調(diào)用test()方法 } }
這種情況下“:test2()方法調(diào)用的test()方法并沒(méi)有異步執(zhí)行,只有一個(gè)線(xiàn)程
AsyncResult
Spring中提供了一個(gè) Future 接口的子類(lèi):AsyncResult,所以我們可以返回 AsyncResult 類(lèi)型的值。
AsyncResult是異步方式,異步主要用于調(diào)用的代碼需要長(zhǎng)時(shí)間運(yùn)行,才能返回結(jié)果的時(shí)候,可以不阻塞調(diào)用者
public class AsyncResult<V> implements ListenableFuture<V> { private final V value; private final ExecutionException executionException; //... }
AsyncResult實(shí)現(xiàn)了ListenableFuture接口,該對(duì)象內(nèi)部有兩個(gè)屬性:返回值和異常信息。
public interface ListenableFuture<T> extends Future<T> { void addCallback(ListenableFutureCallback<? super T> var1); void addCallback(SuccessCallback<? super T> var1, FailureCallback var2); }
AsyncResult代碼示例
獲取異步方法返回值的實(shí)現(xiàn):返回String
public Future<String> test() throws Exception { log.info("開(kāi)始做任務(wù)"); long start = System.currentTimeMillis(); Thread.sleep(1000); long end = System.currentTimeMillis(); log.info("完成任務(wù),耗時(shí):" + (end - start) + "毫秒"); return new AsyncResult<>("任務(wù)完成,耗時(shí)" + (end - start) + "毫秒"); }
返回自定義類(lèi)型的話(huà),在上面的Future里面的String改為對(duì)應(yīng)的實(shí)體類(lèi)
CompletableFuture
統(tǒng)計(jì)一下三個(gè)任務(wù)并發(fā)執(zhí)行共耗時(shí)多少,這就需要等到上述三個(gè)函數(shù)都完成調(diào)動(dòng)之后記錄時(shí)間,并計(jì)算結(jié)果。
也可以使用CompletableFuture來(lái)返回異步調(diào)用的結(jié)果
- 通過(guò)CompletableFuture.allOf(task1, task2, task3).join()實(shí)現(xiàn)三個(gè)異步任務(wù)都結(jié)束之前的阻塞效果
- 三個(gè)任務(wù)都完成之后,根據(jù)結(jié)束時(shí)間 - 開(kāi)始時(shí)間,計(jì)算出三個(gè)任務(wù)并發(fā)執(zhí)行的總耗時(shí)。
CompletableFuture代碼示例
@Async public CompletableFuture<String> doTaskOne() throws Exception { log.info("開(kāi)始做任務(wù)一"); long start = System.currentTimeMillis(); Thread.sleep(random.nextInt(10000)); long end = System.currentTimeMillis(); log.info("完成任務(wù)一,耗時(shí):" + (end - start) + "毫秒"); return CompletableFuture.completedFuture("任務(wù)一完成"); }
@Test public void test() throws Exception { long start = System.currentTimeMillis(); CompletableFuture<String> task1 = asyncTasks.test(); CompletableFuture<String> task2 = asyncTasks.test(); CompletableFuture<String> task3 = asyncTasks.test(); CompletableFuture.allOf(task1, task2, task3).join(); long end = System.currentTimeMillis(); log.info("任務(wù)全部完成,總耗時(shí):" + (end - start) + "毫秒"); }
線(xiàn)程池配置
自行谷歌或百度,spring線(xiàn)程池配置,基本上很詳細(xì)了
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
如何構(gòu)建可重復(fù)讀取inputStream的request
這篇文章主要介紹了如何構(gòu)建可重復(fù)讀取inputStream的request,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03Springboot集成restTemplate過(guò)程詳解
這篇文章主要介紹了Springboot集成restTemplate過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04SpringBoot應(yīng)用線(xiàn)上重啟腳本的命令詳解
這篇文章主要介紹了SpringBoot應(yīng)用線(xiàn)上重啟腳本,通過(guò)查找應(yīng)用進(jìn)程PID,殺死應(yīng)用進(jìn)程PID,運(yùn)行啟動(dòng)腳本,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07Java常用類(lèi)庫(kù)Apache Commons工具類(lèi)說(shuō)明及使用實(shí)例詳解
這篇文章主要介紹了Java常用類(lèi)庫(kù)Apache Commons工具類(lèi)說(shuō)明及使用實(shí)例詳解,需要的朋友可以參考下2020-02-02Java的LinkedHashMap的實(shí)現(xiàn)原理詳解
這篇文章主要介紹了Java的LinkedHashMap的實(shí)現(xiàn)原理詳解,???LinkedHashMap是Map接口的哈希表和鏈接列表實(shí)現(xiàn),具有可預(yù)知的迭代順序,此實(shí)現(xiàn)提供所有可選的映射操作,并允許使用null值和null鍵,此類(lèi)不保證映射的順序,特別是它不保證該順序恒久不變,需要的朋友可以參考下2023-09-09springboot接口參數(shù)校驗(yàn)JSR303的實(shí)現(xiàn)
本文主要介紹了springboot接口參數(shù)校驗(yàn)JSR303的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08