Spring異步任務處理,@Async的配置和使用

這個註解用於標註某個方法或某個類裏面的所有方法都是需要異步處理的。被註解的方法被調用的時候。會在新線程中運行,而調用它的方法會在原來的線程中運行。

這樣可以避免堵塞、以及保證任務的實時性。適用於處理log、發送郵件、短信……等。html


註解的應用範圍:
  • 類:表示這個類中的所有方法都是異步的
  • 方法:表示這種方法是異步的,假設類也註解了。則以這種方法的註解爲準

相關的配置:
<task:annotation-driven />配置:
  • executor:指定一個缺省的executor給@Async使用。
樣例:
<task:annotation-driven executor="asyncExecutor" />

<task:executor />配置參數:
  • id:當配置多個executor時,被@Async("id")指定使用;也被做爲線程名的前綴。

  • pool-size
    • core size:最小的線程數。缺省:1
    • max size:最大的線程數,缺省:Integer.MAX_VALUE
  • queue-capacity:當最小的線程數已經被佔用滿後,新的任務會被放進queue裏面,當這個queue的capacity也被佔滿以後,pool裏面會建立新線程處理這個任務。直到總線程數達到了max size,這時系統會拒絕這個任務並拋出TaskRejectedException異常(缺省配置的狀況下,可以經過rejection-policy來決定怎樣處理這樣的狀況)。缺省值爲:Integer.MAX_VALUE
  • keep-alive:超過core size的那些線程,任務完畢後,再通過這個時長(秒)會被結束掉
  • rejection-policy:當pool已經達到max size的時候,怎樣處理新任務
    • ABORT(缺省):拋出TaskRejectedException異常,而後不運行
    • DISCARD:不運行,也不拋出異常
    • DISCARD_OLDEST:丟棄queue中最舊的那個任務
    • CALLER_RUNS:不在新線程中運行任務,而是有調用者所在的線程來運行

配置樣例:
 <task:annotation-driven executor="asyncExecutor" />
 <task:executor id="asyncExecutor" pool-size=" 100- 10000" queue-capacity=" 10"/>

實例:
 <!-- 缺省的異步任務線程池 --> 
 <task:annotation-driven executor="asyncExecutor" />
 <task:executor id="asyncExecutor" pool-size="100-10000" queue-capacity="10" />

 <!-- 處理log的線程池 -->
 <task:executor id="logExecutor" pool-size="15-1000" queue-capacity="5" keep-alive="5"/>

 @Override
 @Async("logExecutor")    //假設不指定名字。會使用缺省的「asyncExecutor」
 public void saveUserOpLog(TabUserOpLog tabUserOpLog) {
 
  userOpLogDAO.insertTabUserOpLog(tabUserOpLog);
 }
(注意:假設在同一個類中調用的話。不會生效,緣由請參考: http://blog.csdn.net/clementad/article/details/47339519

經過log可以看到,已經分開兩個線程運行:


線程的優先級和類型:
優先級:NORM_PRIORITY
類型:非守護線程

用戶線程(User Thread):JVM會等待所有的用戶線程結束後才退出;當系統中沒實用戶線程了,JVM也就退出了
守護線程(Daemon Thread):一般是爲其它線程提供服務的線程。比方GC垃圾回收器;JVM退出時,不會管守護線程是否存在,而是直接退出
因此,對於文件、數據庫的操做。適宜使用守護線程,否則可能會丟失數據!

Web應用中止時,Spring容器會被關閉。調用者假設是Spring bean。就會中止生成新任務。

然而,線程池中已經在執行的任務。由於缺省是用戶線程,因此JVM會等待它們結束後才退出。java


附:Java編程方式的配置方法:
@Configuration
@EnableAsync
public class SpringConfig {

	/** Set the ThreadPoolExecutor's core pool size. */
	private int corePoolSize = 10;
	/** Set the ThreadPoolExecutor's maximum pool size. */
	private int maxPoolSize = 200;
	/** Set the capacity for the ThreadPoolExecutor's BlockingQueue. */
	private int queueCapacity = 10;

	private String ThreadNamePrefix = "MyLogExecutor-";

	@Bean
	public Executor logExecutor() {
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setCorePoolSize(corePoolSize);
		executor.setMaxPoolSize(maxPoolSize);
		executor.setQueueCapacity(queueCapacity);
		executor.setThreadNamePrefix(ThreadNamePrefix);

		// rejection-policy:當pool已經達到max size的時候,怎樣處理新任務
		// CALLER_RUNS:不在新線程中運行任務。而是有調用者所在的線程來運行
		executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
		executor.initialize();
		return executor;
	}

}



(原創文章。轉載請註明轉自Clement-Xu的博客:http://blog.csdn.net/clementad/article/details/47403185
相關文章
相關標籤/搜索