SpringBoot 進階系列二 @EnableAsync和@Async 執行緒池定義和使用
在spring傳統自定義執行緒池,是在xml配置檔案中進行配置如:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"> <!-- 開啟非同步,並引入執行緒池 --> <task:annotation-driven executor="threadPool" /> <!-- 定義執行緒池 --> <bean id="threadPool" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <!-- 核心執行緒數,預設為1 --> <property name="corePoolSize" value="5" /> <!-- 最大執行緒數,預設為Integer.MAX_VALUE --> <property name="maxPoolSize" value="20" /> <!-- 佇列最大長度,一般需要設定值>=notifyScheduledMainExecutor.maxNum;預設為Integer.MAX_VALUE --> <property name="queueCapacity" value="500" /> <!-- 執行緒池維護執行緒所允許的空閒時間,預設為60s --> <property name="keepAliveSeconds" value="30" /> <!-- 完成任務自動關閉 , 預設為false--> <property name="waitForTasksToCompleteOnShutdown" value="true" /> <!-- 核心執行緒超時退出,預設為false --> <property name="allowCoreThreadTimeOut" value="true" /> <!-- 執行緒池對拒絕任務(無執行緒可用)的處理策略,目前只支援AbortPolicy、CallerRunsPolicy;預設為後者 --> <property name="rejectedExecutionHandler"> <!-- AbortPolicy:直接丟擲java.util.concurrent.RejectedExecutionException異常 --> <!-- CallerRunsPolicy:主執行緒直接執行該任務,執行完之後嘗試新增下一個任務到執行緒池中,可以有效降低向執行緒池內新增任務的速度 --> <!-- DiscardOldestPolicy:拋棄舊的任務、暫不支援;會導致被丟棄的任務無法再次被執行 --> <!-- DiscardPolicy:拋棄當前任務、暫不支援;會導致被丟棄的任務無法再次被執行 --> <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" /> </property> </bean> </beans>
在springboot框架中實際上也可以這樣去配置,但是推薦使用註解的方式去完成,會更加的直觀
首先建立一個類 :ThreadPoolTaskConfig
import java.util.concurrent.ThreadPoolExecutor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; /** * 執行緒池配置 * @author jim * */ @Configuration @EnableAsync public class ThreadPoolTaskConfig { private static final int corePoolSize = 10; // 核心執行緒數(預設執行緒數) private static final int maxPoolSize = 100; // 最大執行緒數 private static final int keepAliveTime = 10; // 允許執行緒空閒時間(單位:預設為秒) private static final int queueCapacity = 200; // 緩衝佇列數 private static final String threadNamePrefix = "Async-Service-"; // 執行緒池名字首 @Bean("taskExecutor") // bean的名稱,預設為首字母小寫的方法名 public ThreadPoolTaskExecutor taskExecutor(){ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(corePoolSize); executor.setMaxPoolSize(maxPoolSize); executor.setQueueCapacity(queueCapacity); executor.setKeepAliveSeconds(keepAliveTime); executor.setThreadNamePrefix(threadNamePrefix); // 執行緒池對拒絕任務的處理策略 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 初始化 executor.initialize(); return executor; } }
注① @Configuration用於定義配置類,可替換xml配置檔案,被註解的類內部包含有一個或多個被@Bean註解的方法,這些方法將會被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext類進行掃描,並用於構建bean定義,初始化Spring容器。
注② @EnableAsync開始對非同步任務的支援
接著寫個service類 testAsyncService :
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service public class testAsyncService { Logger log = LoggerFactory.getLogger(testAsyncService.class); // 傳送提醒簡訊 1 @Async("taskExecutor") public void service1() throws InterruptedException { log.info("--------start-service1------------"); Thread.sleep(5000); // 模擬耗時 log.info("--------end-service1------------"); } // 傳送提醒簡訊 2 @Async("taskExecutor") public void service2() throws InterruptedException { log.info("--------start-service2------------"); Thread.sleep(2000); // 模擬耗時 log.info("--------end-service2------------"); } }
注① 使用@Async註解來宣告一個或多個非同步任務,可以加在方法或者類上,加在類上表示這整個類都是使用這個自定義執行緒池進行操作
接著我們可以建立control類@Autowired這個service並且呼叫這其中兩個方法,進行連續呼叫,會發現執行結果是
--------start-service1------------
--------start-service2------------
--------end-service2------------
--------end-service1------------
可以說明我們的非同步執行成功了
注意點:
一、非同步方法使用static修飾
二、非同步類沒有使用@Component註解(或其他註解)導致spring無法掃描到非同步類
三、類中需要使用@Autowired或@Resource等註解自動注入,不能自己手動new物件
五、如果使用SpringBoot框架必須在啟動類中增加@EnableAsync註解