本文轉自http://blog.csdn.net/clementad/article/details/47403185 感謝作者
這個注解用於標注某個方法或某個類里面的所有方法都是需要異步處理的。被注解的方法被調用的時候,會在新線程中執行,而調用它的方法會在原來的線程中執行。這樣可以避免阻塞、以及保證任務的實時性。適用於處理log、發送郵件、短信……等。
注解的應用范圍:
- 類:表示這個類中的所有方法都是異步的
- 方法:表示這個方法是異步的,如果類也注解了,則以這個方法的注解為准
相關的配置:
<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);
- }
通過log可以看到,已經分開兩個線程執行:

線程的優先級和類型:
優先級:NORM_PRIORITY
類型:非守護線程
用戶線程(User Thread):JVM會等待所有的用戶線程結束后才退出;當系統中沒有用戶線程了,JVM也就退出了
守護線程(Daemon Thread):一般是為其他線程提供服務的線程,比如GC垃圾回收器;JVM退出時,不會管守護線程是否存在,而是直接退出
所以,對於文件、數據庫的操作,不適宜使用守護線程,不然可能會丟失數據!
Web應用停止時,Spring容器會被關閉,調用者如果是Spring bean,就會停止生成新任務。然而,線程池中已經在運行的任務,由於缺省是用戶線程,所以JVM會等待它們結束后才退出。
附: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;
- }
- }