1. 程式人生 > >SpringBoot 進階系列二 @EnableAsync和@Async 執行緒池定義和使用

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註解