您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“@Async怎么用”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“@Async怎么用”這篇文章吧。
使用@Async注解的方法會新開一個線程去執(zhí)行該方法,一些比較耗時的操作的操作使用該注解,交由Spring去管理線程的創(chuàng)建與銷毀。
接下來闡述一下自己的使用心得:
一、啟用@Async,個人在Springboot的啟動類加上以下注解
@EnableAsync
二、在需要進(jìn)行異步操作的方法上加上@Async注解,注解不起作用的主要原因是被調(diào)用方法和調(diào)用方法處理同一個類中。 失效的代碼
class TestService { void a() { this.b(); } @Async void b(){} }
正常的代碼
class TestService { void a(){ BService.b(); } } class BService() { @Async void b(){} }
這個時候我們可能會想到AopContext.currentProxy(),這樣就可以在同一個方法里面調(diào)用了
class TestService { TestService getSelf() { return (TestService)AopContext.currentProxy(); } void a() { getSelf().b(); } @Async void b(){} }
在這里可能會拋出以下的異常,具體的原因及解決思路請參考這篇文章
java.lang.IllegalStateException: Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.
三、接下來了解一下@Async使用的線程池
@Async異步方法默認(rèn)使用Spring創(chuàng)建ThreadPoolTaskExecutor(參考TaskExecutionAutoConfiguration),其中默認(rèn)核心線程數(shù)為8,默認(rèn)最大隊列和默認(rèn)最大線程數(shù)都是Integer.MAX_VALUE。創(chuàng)建新線程的條件是隊列填滿時,而這樣的配置隊列永遠(yuǎn)不會填滿,如果有@Async注解標(biāo)注的方法長期占用線程(比如HTTP長連接等待獲取結(jié)果),在核心8個線程數(shù)占用滿了之后,新的調(diào)用就會進(jìn)入隊列, 外部表現(xiàn)為沒有執(zhí)行。
我們可以自定義一個線程池,線程數(shù)的設(shè)定需要考慮一下要執(zhí)行的任務(wù)是IO密集型任務(wù),還是CPU密集型任務(wù)。對于CPU密集型任務(wù),如CPU核數(shù)+1;對于IO密集型任務(wù),由于IO密集型任務(wù)線程并不是一直在執(zhí)行任務(wù),則應(yīng)配置盡可能多的線程,如CPU核數(shù)*2。
接下來給出一個IO密集型任務(wù)的線程池配置代碼
@Configuration public class ThreadPoolConfig { /** * 核心線程數(shù) */ private static final int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors() * 2; /** * 最大線程數(shù) */ private static final int MAX_POOL_SIZE = CORE_POOL_SIZE * 4 < 256 ? 256 : CORE_POOL_SIZE * 4; /** * 允許線程空閑時間(單位為秒) */ private static final int KEEP_ALIVE_TIME = 10; /** * 緩沖隊列數(shù) */ private static final int QUEUE_CAPACITY = 200; /** * 線程池中任務(wù)的等待時間,如果超過這個時候還沒有銷毀就強制銷毀 */ private static final int AWAIT_TERMINATION = 60; /** * 用來設(shè)置線程池關(guān)閉的時候等待所有任務(wù)都完成再繼續(xù)銷毀其他的Bean */ private static final Boolean WAIT_FOR_TASKS_TO_COMPLETE_ON_SHUTDOWN = true; /** * 線程池名前綴 */ private static final String THREAD_NAME_PREFIX = "Spider-ThreadPool-"; @Bean("spiderTaskExecutor") public ThreadPoolTaskExecutor spiderTaskExecutor () { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); taskExecutor.setCorePoolSize(CORE_POOL_SIZE); taskExecutor.setMaxPoolSize(MAX_POOL_SIZE); taskExecutor.setKeepAliveSeconds(KEEP_ALIVE_TIME); taskExecutor.setQueueCapacity(QUEUE_CAPACITY); taskExecutor.setThreadNamePrefix(THREAD_NAME_PREFIX); taskExecutor.setWaitForTasksToCompleteOnShutdown(WAIT_FOR_TASKS_TO_COMPLETE_ON_SHUTDOWN); taskExecutor.setAwaitTerminationSeconds(AWAIT_TERMINATION); /** * 拒絕策略 => 當(dāng)pool已經(jīng)達(dá)到max size的時候,如何處理新任務(wù) * CALLER_RUNS:不在新線程中執(zhí)行任務(wù),而是由調(diào)用者所在的線程來執(zhí)行 * AbortPolicy:直接拋出異常,這是默認(rèn)策略; * DiscardOldestPolicy:丟棄阻塞隊列中靠最前的任務(wù),并執(zhí)行當(dāng)前任務(wù); * DiscardPolicy:直接丟棄任務(wù); */ taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); taskExecutor.initialize(); return taskExecutor; } }
線程池的使用
@Async("spiderTaskExecutor")
最后補充一些知識,要合理的控制線程數(shù)(比如采集訂單信息的同時要采集訂單詳情和文章信息,訂單詳情和文章信息可以合并在一個線程中處理),不要濫用。需要考慮什么時候使用MQ,什么時候開啟線程異步處理。推薦一個分析jstack文件的工具,IBM Thread and Monitor Dump Analyzer for Java,分析一下正在運行、發(fā)生死鎖、等待、阻塞的線程。
以上是“@Async怎么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。