面試問,@EnableAsync 和 @Async
在我們的日常開發(fā)中,我們偶爾會遇到在業(yè)務層中我們需要同時修改多張表的數(shù)據(jù)并且需要有序的執(zhí)行,如果我們用往常的同步的方式,也就是單線程的方式來執(zhí)行的話,可能會出現(xiàn)執(zhí)行超時等異常造成請求結(jié)果失敗,及時成功,前端也需要等待較長時間來獲取響應結(jié)果,這樣不但造成了用戶體驗差,而且會經(jīng)常出現(xiàn)請求執(zhí)行失敗的問題,在這里我們一般會采用3種方式來處理,如下所示:
在采用三種方式之前,我們所有來觀察一下使用同步的方式實現(xiàn)的結(jié)果:
1.我們定義一個TestController,如下所示:
@RestController
public?class?TestController?{
?
????@Autowired
????private?TestService?service;
????/**
?????*?使用傳統(tǒng)方式測試
?????*/
????@GetMapping("/test")
????public?void?test()?{
????????System.out.println("獲取主線程名稱:"?+?Thread.currentThread().getName());
????????service.serviceTest();
????????System.out.println("執(zhí)行成功,返回結(jié)果");
????}
}
2. 我們定義TestService,如下所示:
@Service
public?class?TestAsyncService?{
?
????public?void?serviceTest()?{
????????????//?這里執(zhí)行實際的業(yè)務邏輯,在這里我們就是用一個簡單的遍歷來模擬
????????????Arrays.stream(new?int[]{1,2,3,4,5,6,7,8,9,10}).forEach(?t?->?{
????????????????try?{
????????????????????Thread.sleep(100);
????????????????}?catch?(InterruptedException?e)?{
????????????????????e.printStackTrace();
????????????????}
????????????????System.out.println("獲取number為:"?+?t)?;
????????????});
????}
}
我們執(zhí)行請求http://localhost:8888/test,結(jié)果顯示如下:

接下來我們采用我們所說的3種方式來實現(xiàn):
1.使用線程池的方式來實現(xiàn)
1)定義TestAsyncController中的test()方法,如下所示:
@RestController
public?class?TestAsyncController?{
?
????@Autowired
????private?TestAsyncService?asyncService;
????
????/**
?????*?使用傳統(tǒng)方式測試
?????*/
????@GetMapping("/test")
????public?void?test()?{
????????System.out.println("獲取主線程名稱:"?+?Thread.currentThread().getName());
????????/**
?????????*??這里也可以采用以下方式使用,但是使用線程池的方式可以很便捷的對線程管理(提高程序的整體性能),
?????????*??也可以減少每次執(zhí)行該請求時都需要創(chuàng)建一個線程造成的性能消耗
?????????*??new?Thread(()?->{
?????????*??run方法中的業(yè)務邏輯
?????????*??})
?????????*/
?
????????/**
?????????*?定義一個線程池
?????????*?核心線程數(shù)(corePoolSize):1
?????????*?最大線程數(shù)(maximumPoolSize):?1
?????????*?保持連接時間(keepAliveTime):50000
?????????*?時間單位?(TimeUnit):TimeUnit.MILLISECONDS(毫秒)
?????????*?阻塞隊列?new?LinkedBlockingQueue()
?????????*/
????????ThreadPoolExecutor?executor?=?new?ThreadPoolExecutor(1,5,50000,?TimeUnit.MILLISECONDS,?new?LinkedBlockingQueue());
????????//?執(zhí)行業(yè)務邏輯方法serviceTest()
????????executor.execute(new?Runnable()?{
????????????@Override
????????????public?void?run()?{
????????????????asyncService.serviceTest();
????????????}
????????});
????????System.out.println("執(zhí)行完成,向用戶響應成功信息");
????}
}
2)定義TestAsyncService和TestService中的test()相同,在這就不顯示了;
3)查看運行結(jié)果:

我們發(fā)現(xiàn)在主線程中使用線程池中的線程來實現(xiàn),程序的執(zhí)行結(jié)果表明,主線程直接將結(jié)果進行了返回,然后才是線程池在執(zhí)行業(yè)務邏輯,減少了請求響應時長。
2. 使用注解@EnableAsync和@Async來實現(xiàn)
(1)雖然這樣實現(xiàn)了我們想要的結(jié)果,但是,但是我們發(fā)現(xiàn)如果我們在多個請求中都需要這種異步請求,每次都寫這么冗余的線程池配置會不會,這種問題當然會被我們強大的spring所觀察到,所以spring為了提升開發(fā)人員的開發(fā)效率,使用@EnableAsync來開啟異步的支持,使用@Async來對某個方法進行異步執(zhí)行。
TestAsyncController如下所示:
@RestController
public?class?TestAsyncController?{
?
????@Autowired
????private?TestAsyncService?asyncService;
?
????/**
?????*?使用@Async來實現(xiàn)
?????*/
????@GetMapping("/test")
????public?void?test()?{
????????System.out.println("獲取主線程名稱:"?+?Thread.currentThread().getName());
????????//?異步調(diào)用
????????asyncService.serviceTest();
????????System.out.println("執(zhí)行完成,向用戶響應成功信息");
????}
(2)TestAsyncService如下所示:
@Service
@EnableAsync
public?class?TestAsyncService?{
?
?
????/**
?????*?采用異步執(zhí)行
?????*/
????@Async
????public?void?serviceTest()?{
????????????//?這里執(zhí)行實際的業(yè)務邏輯,在這里我們就是用一個簡單的遍歷來模擬
????????????Arrays.stream(new?int[]{1,2,3,4,5,6,7,8,9,10}).forEach(?t?->?{
????????????????try?{
????????????????????Thread.sleep(100);
????????????????}?catch?(InterruptedException?e)?{
????????????????????e.printStackTrace();
????????????????}
????????????????System.out.println("獲取number為:"?+?t)?;
????????????});
????}
(3)執(zhí)行結(jié)果如下:

結(jié)果與使用線程池的結(jié)果一致,但是簡化了我們編寫代碼的邏輯,@Async注解幫我們實現(xiàn)了創(chuàng)建線程池的繁瑣,提高了我們的開發(fā)效率。
3. 使用消息隊列(mq)來實現(xiàn)
當我們涉及的請求在業(yè)務邏輯中一次性操作很多很多的數(shù)據(jù),例如:一個請求執(zhí)行相關業(yè)務操作后,將操作日志插入到數(shù)據(jù)庫中,我們可以使用@Async來實現(xiàn),但是這樣增加了業(yè)務和非業(yè)務關系的冗余性(同時如何并發(fā)量很大,我們使用@Async處理,無法提升我們系統(tǒng)的整體系統(tǒng),這樣很容易造成服務器宕機),所以我們對于這種情況,我們會采用mq來實現(xiàn),將業(yè)務邏輯和非業(yè)務邏輯進行隔離執(zhí)行,互不影響,非業(yè)務邏輯不會影響到執(zhí)行業(yè)務邏輯的結(jié)果和主機性能,對于mq的處理,我會在后續(xù)添加。
來源:blog.csdn.net/qq_38796327/article/details/90599867
歡迎關注“Java引導者”,我們分享最有價值的Java的干貨文章,助力您成為有思想的Java開發(fā)工程師!
