異步調(diào)用就是不用等待結(jié)果的返回就執(zhí)行后面的邏輯,同步調(diào)用則需要等帶結(jié)果再執(zhí)行后面的邏輯。
通常我們使用異步操作都會(huì)去創(chuàng)建一個(gè)線程執(zhí)行一段邏輯,然后把這個(gè)線程丟到線程池中去執(zhí)行,代碼如下:
1
2
3
4
5
6
7
8
9
|
ExecutorService executorService = Executors.newFixedThreadPool( 10 ); executorService.execute(() -> { try { // 業(yè)務(wù)邏輯 } catch (Exception e) { e.printStackTrace(); } finally { } }); |
這樣的方式看起來沒那么優(yōu)雅,盡管用了java的lambda。在Spring Boot中有一種更簡單的方式來執(zhí)行異步操作,只需要一個(gè)@Async注解即可。
1
2
3
4
|
@Async public void saveLog() { System.err.println(Thread.currentThread().getName()); } |
我們可以直接在Controller中調(diào)用這個(gè)業(yè)務(wù)方法,它就是異步執(zhí)行的,會(huì)在默認(rèn)的線程池中去執(zhí)行。需要注意的是一定要在外部的類中去調(diào)用這個(gè)方法,如果在本類調(diào)用是不起作用的,比如this.saveLog()。 最后在啟動(dòng)類上開啟異步任務(wù)的執(zhí)行,添加@EnableAsync即可。
另外關(guān)于執(zhí)行異步任務(wù)的線程池我們也可以自定義,首先我們定義一個(gè)線程池的配置類,用來配置一些參數(shù),具體代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; /** * 異步任務(wù)線程池配置 * * @author yinjihuan */ @Configuration @ConfigurationProperties (prefix = "spring.task.pool" ) public class TaskThreadPoolConfig { //核心線程數(shù) private int corePoolSize = 5 ; //最大線程數(shù) private int maxPoolSize = 50 ; //線程池維護(hù)線程所允許的空閑時(shí)間 private int keepAliveSeconds = 60 ; //隊(duì)列長度 private int queueCapacity = 10000 ; //線程名稱前綴 private String threadNamePrefix = "FSH-AsyncTask-" ; public String getThreadNamePrefix() { return threadNamePrefix; } public void setThreadNamePrefix(String threadNamePrefix) { this .threadNamePrefix = threadNamePrefix; } public int getCorePoolSize() { return corePoolSize; } public void setCorePoolSize( int corePoolSize) { this .corePoolSize = corePoolSize; } public int getMaxPoolSize() { return maxPoolSize; } public void setMaxPoolSize( int maxPoolSize) { this .maxPoolSize = maxPoolSize; } public int getKeepAliveSeconds() { return keepAliveSeconds; } public void setKeepAliveSeconds( int keepAliveSeconds) { this .keepAliveSeconds = keepAliveSeconds; } public int getQueueCapacity() { return queueCapacity; } public void setQueueCapacity( int queueCapacity) { this .queueCapacity = queueCapacity; } } |
然后我們重新定義線程池的配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
import java.lang.reflect.Method; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Configuration public class AsyncTaskExecutePool implements AsyncConfigurer { private Logger logger = LoggerFactory.getLogger(AsyncTaskExecutePool. class ); @Autowired private TaskThreadPoolConfig config; @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(config.getCorePoolSize()); executor.setMaxPoolSize(config.getMaxPoolSize()); executor.setQueueCapacity(config.getQueueCapacity()); executor.setKeepAliveSeconds(config.getKeepAliveSeconds()); executor.setThreadNamePrefix(config.getThreadNamePrefix()); //線程池對(duì)拒絕任務(wù)(無線程可用)的處理策略,目前只支持AbortPolicy、CallerRunsPolicy //AbortPolicy:直接拋出java.util.concurrent.RejectedExecutionException異常 --> //CallerRunsPolicy:主線程直接執(zhí)行該任務(wù),執(zhí)行完之后嘗試添加下一個(gè)任務(wù)到線程池中,可以有效降低向線程池內(nèi)添加任務(wù)的速度 --> //DiscardOldestPolicy:拋棄舊的任務(wù)、暫不支持;會(huì)導(dǎo)致被丟棄的任務(wù)無法再次被執(zhí)行 --> //DiscardPolicy:拋棄當(dāng)前任務(wù)、暫不支持;會(huì)導(dǎo)致被丟棄的任務(wù)無法再次被執(zhí)行 --> executor.setRejectedExecutionHandler( new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { // 異步任務(wù)中異常處理 return new AsyncUncaughtExceptionHandler() { @Override public void handleUncaughtException(Throwable arg0, Method arg1, Object... arg2) { logger.error( "==========================" +arg0.getMessage()+ "=======================" , arg0); logger.error( "exception method:" + arg1.getName()); } }; } } |
配置完之后我們的異步任務(wù)執(zhí)行的線程池就是我們自定義的了,我們可以通過在屬性文件里面配置線程池的大小等等信息,也可以使用默認(rèn)的配置:
spring.task.pool.maxPoolSize=100
最后講下線程池配置的拒絕策略,當(dāng)我們的線程數(shù)量高于線程池的處理速度時(shí),任務(wù)會(huì)被緩存到本地的隊(duì)列中,隊(duì)列也是有大小的,如果超過了這個(gè)大小,我們需要有拒絕的策略,不然就會(huì)內(nèi)存溢出了,目前支持2種拒絕策略:
- AbortPolicy: 直接拋出java.util.concurrent.RejectedExecutionException異常
- CallerRunsPolicy: 主線程直接執(zhí)行該任務(wù),執(zhí)行完之后嘗試添加下一個(gè)任務(wù)到線程池中,可以有效降低向線程池內(nèi)添加任務(wù)的速度
- 建議大家用CallerRunsPolicy策略,因?yàn)楫?dāng)隊(duì)列中的任務(wù)滿了之后,如果直接拋異常,那么這個(gè)任務(wù)就會(huì)被丟棄,如果是CallerRunsPolicy策略會(huì)用主線程去執(zhí)行,就是同步執(zhí)行,最起碼這樣任務(wù)不會(huì)丟棄。
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。
原文鏈接:https://blog.51cto.com/14888386/2516391