Springboot中的@EnableAsync和@Async的作用和基本用法




在我們的日常開發中,我們偶爾會遇到在業務層中我們需要同時修改多張表的數據並且需要有序的執行,如果我們用往常的同步的方式,也就是單線程的方式來執行的話,可能會出現執行超時等異常造成請求結果失敗,及時成功,前端也需要等待較長時間來獲取響應結果,這樣不但造成了用戶體驗差,而且會經常出現請求執行失敗的問題,在這里我們一般會采用3種方式來處理,如下所示:

在采用三種方式之前,我們所有來觀察一下使用同步的方式實現的結果:

1.我們定義一個TestController,如下所示:

@RestController
public class TestController {

@Autowired
private TestService service;


/**
* 使用傳統方式測試
*/
@GetMapping("/test")
public void test() {
System.out.println("獲取主線程名稱:" + Thread.currentThread().getName());
service.serviceTest();
System.out.println("執行成功,返回結果");
}
}
2. 我們定義TestService,如下所示:

@Service
public class TestAsyncService {

public void serviceTest() {
// 這里執行實際的業務邏輯,在這里我們就是用一個簡單的遍歷來模擬
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) ;
});
}
}
我們執行請求http://localhost:8888/test,結果顯示如下:

 

接下來我們采用我們所說的3種方式來實現:

1.使用線程池的方式來實現

.(1)定義TestAsyncController中的test()方法,如下所示:

@RestController
public class TestAsyncController {

@Autowired
private TestAsyncService asyncService;

/**
* 使用傳統方式測試
*/
@GetMapping("/test")
public void test() {
System.out.println("獲取主線程名稱:" + Thread.currentThread().getName());
/**
* 這里也可以采用以下方式使用,但是使用線程池的方式可以很便捷的對線程管理(提高程序的整體性能),
* 也可以減少每次執行該請求時都需要創建一個線程造成的性能消耗
* new Thread(() ->{
* run方法中的業務邏輯
* })
*/

/**
* 定義一個線程池
* 核心線程數(corePoolSize):1
* 最大線程數(maximumPoolSize): 1
* 保持連接時間(keepAliveTime):50000
* 時間單位 (TimeUnit):TimeUnit.MILLISECONDS(毫秒)
* 阻塞隊列 new LinkedBlockingQueue<Runnable>()
*/
ThreadPoolExecutor executor = new ThreadPoolExecutor(1,5,50000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
// 執行業務邏輯方法serviceTest()
executor.execute(new Runnable() {
@Override
public void run() {
asyncService.serviceTest();
}
});
System.out.println("執行完成,向用戶響應成功信息");
}


}
(2).定義TestAsyncService和TestService中的test()相同,在這就不顯示了;

(3).查看運行結果:

 

我們發現在主線程中使用線程池中的線程來實現,程序的執行結果表明,主線程直接將結果進行了返回,然后才是線程池在執行業務邏輯,減少了請求響應時長。

2. 使用注解@EnableAsync和@Async來實現 

(1)。雖然這樣實現了我們想要的結果,但是,但是我們發現如果我們在多個請求中都需要這種異步請求,每次都寫這么冗余的線程池配置會不會,這種問題當然會被我們強大的spring所觀察到,所以spring為了提升開發人員的開發效率,使用@EnableAsync來開啟異步的支持,使用@Async來對某個方法進行異步執行。TestAsyncController如下所示:

@RestController
public class TestAsyncController {

@Autowired
private TestAsyncService asyncService;

/**
* 使用@Async來實現
*/
@GetMapping("/test")
public void test() {
System.out.println("獲取主線程名稱:" + Thread.currentThread().getName());
// 異步調用
asyncService.serviceTest();
System.out.println("執行完成,向用戶響應成功信息");
}
(2)TestAsyncService如下所示:

@Service
@EnableAsync
public class TestAsyncService {


/**
* 采用異步執行
*/
@Async
public void serviceTest() {
// 這里執行實際的業務邏輯,在這里我們就是用一個簡單的遍歷來模擬
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)執行結果如下:

 

結果與使用線程池的結果一致,但是簡化了我們編寫代碼的邏輯,@Async注解幫我們實現了創建線程池的繁瑣,提高了我們的開發效率。

3. 使用消息隊列(mq)來實現

當我們涉及的請求在業務邏輯中一次性操作很多很多的數據,例如:一個請求執行相關業務操作后,將操作日志插入到數據庫中,我們可以使用@Async來實現,但是這樣增加了業務和非業務關系的冗余性(同時如何並發量很大,我們使用@Async處理,無法提升我們系統的整體系統,這樣很容易造成服務器宕機),所以我們對於這種情況,我們會采用mq來實現,將業務邏輯和非業務邏輯進行隔離執行,互不影響,非業務邏輯不會影響到執行業務邏輯的結果和主機性能,對於mq的處理,我會在后續添加。

未完待續。。。 
————————————————

原文鏈接:https://blog.csdn.net/qq_38796327/article/details/90599867


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM