當我們需要提高系統的並行效能時,我們可以將耗時的操作非同步執行,從而避免執行緒阻塞,提高系統的並行效能。例如,在處理大量的並行請求時,如果每個請求都是同步阻塞的方式處
理,系統的響應時間會變得很長。而使用非同步程式設計,可以將一些耗時的操作交給其他執行緒去處理,從而釋放主執行緒,提高系統的並行能力。
從Spring 3開始,可以通過在方法上標註@Async
註解來實現非同步方法呼叫。這意味著當我們呼叫被@Async
註解修飾的方法時,它會在後臺以非同步方式執行。為了啟用非同步功能,我們需要
一個設定類,並在該類上使用@EnableAsync
註解。這個註解告訴Spring要開啟非同步功能。
使用@EnableAsync
來開啟非同步任務支援,@EnableAsync
註解可以直接放在SpringBoot啟動類上,也可以單獨放在其他設定類上。這裡選擇使用單獨的設定類SyncConfiguration
。
使用@Async
註解,在預設情況下用的是SimpleAsyncTaskExecutor執行緒池,該執行緒池不是真正意義上的執行緒池。
使用此執行緒池無法實現執行緒重用,每次呼叫都會新建一條執行緒。若系統中不斷的建立執行緒,最終會導致系統佔用記憶體過高,引發OutOfMemoryError
錯誤,所以在使用Spring中的@Async非同步
框架時要自定義執行緒池,替代預設的SimpleAsyncTaskExecutor,這也是自定義設定的意義之一。
@Configuration @EnableAsync public class SyncConfiguration { @Bean(name = "asyncPoolTaskExecutor") public ThreadPoolTaskExecutor executor() { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); //核心執行緒數,設定核心執行緒數。核心執行緒數是執行緒池中一直保持活動的執行緒數量,即使它們是空閒的。 taskExecutor.setCorePoolSize(10); //設定執行緒池維護執行緒的最大數量。當緩衝佇列已滿並且核心執行緒數的執行緒都在忙碌時,執行緒池會建立新的執行緒,直到達到最大執行緒數。 taskExecutor.setMaxPoolSize(100); //設定緩衝佇列的容量。當所有的核心執行緒都在忙碌時,新的任務將會被放入緩衝佇列中等待執行。 taskExecutor.setQueueCapacity(50); //設定非核心執行緒的空閒時間。當超過核心執行緒數的執行緒在空閒時間達到設定值後,它們將被銷燬,以減少資源的消耗。 taskExecutor.setKeepAliveSeconds(200); //非同步方法內部執行緒名稱 taskExecutor.setThreadNamePrefix("async-"); /** * 當執行緒池的任務快取佇列已滿並且執行緒池中的執行緒數目達到maximumPoolSize,如果還有任務到來就會採取任務拒絕策略 * 通常有以下四種策略: * ThreadPoolExecutor.AbortPolicy:丟棄任務並丟擲RejectedExecutionException異常。 * ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不丟擲異常。 * ThreadPoolExecutor.DiscardOldestPolicy:丟棄佇列最前面的任務,然後重新嘗試執行任務(重複此過程) * ThreadPoolExecutor.CallerRunsPolicy:重試新增當前的任務,自動重複呼叫 execute() 方法,直到成功 */ taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); taskExecutor.initialize(); return taskExecutor; } }
Spring提供了多種執行緒池:
SimpleAsyncTaskExecutor
:不是真的執行緒池,這個類不重用執行緒,每次呼叫都會建立一個新的執行緒。
SyncTaskExecutor
:這個類沒有實現非同步呼叫,只是一個同步操作。只適用於不需要多執行緒的地
ConcurrentTaskExecutor
:Executor的適配類,不推薦使用。如果ThreadPoolTaskExecutor不滿足要求時,才用考慮使用這個類
ThreadPoolTaskScheduler
:可以使用cron表示式
ThreadPoolTaskExecutor
:最常使用,推薦。 其實質是對java.util.concurrent.ThreadPoolExecutor的包裝
在非同步處理的方法上新增@Async
註解,代表該方法為非同步處理。
public class AsyncTask { @Async public void Task() { long t1 = System.currentTimeMillis(); Thread.sleep(5000); long t2 = System.currentTimeMillis(); log.info("task cost {} ms" , t2-t1); }
asyncTask.Task();
4. @Async
的原理當一個帶有@Async
註解的方法被呼叫時,Spring會建立一個非同步代理物件來代理這個方法的呼叫。
非同步代理物件會將方法呼叫封裝為一個獨立的任務,並將該任務提交給非同步任務執行器。
非同步任務執行器從執行緒池中獲取一個空閒的執行緒,並將任務分配給該執行緒執行。
呼叫執行緒立即返回,不會等待非同步任務的執行完成。
非同步任務在獨立的執行緒中執行,直到任務完成。
非同步任務執行完成後,可以選擇返回結果或者不返回任何結果。