線程池

1 基礎(chǔ)

Java提供的Thread需要寫一堆的代碼,用了spring,想讓哪個方法是異步的,加個注解,就搞定了。

spring AOP,會主動攔截添加注解@Scheduled/@Async的方法,然后,由spring維護線程的整個生命周期。

  • 定時任務(wù)@Schedule
@Scheduled(cron = "0/20 * * * * ?") //每20秒執(zhí)行一次
  • 異步任務(wù)@Async

釋義:開啟異步任務(wù),執(zhí)行標記的方法。方法看起來還是普通的方法,但是呢,調(diào)用的時候,spring會主動開啟新的線程。


思考:如果,開啟的線程數(shù)量太多,服務(wù)器處理不完,怎么辦?

答:類似jdbc的Connection。同樣的道理,創(chuàng)建一個線程池,需要線程的時候,從池子里出來,用完再放回去;得不到服務(wù)的線程,不能直接丟棄,還需要再維護一個任務(wù)隊列;如果排隊的任務(wù)超出了隊列的范圍,那就得考慮擴容了,調(diào)參、或是多加臺服務(wù)器、或者直接拒絕。

2 進階:共用一個Thread Pool

method上添加的注解 Thread Pool class configuaration
@Schedule ThreadPoolTaskScheduler @EnableScheduling
@Async ThreadPoolTaskExecutor @EnableAsync

2.1 配置bean

@Configuration //添加這個注解的class,只在容器啟動的時候加載一次
@EnableAsync //啟用異步任務(wù):TaskExecutor
@EnableScheduling //啟用定時任務(wù):TaskScheduler
public class CommonBeanConfigure {
 
    @Bean("asyncExecutor") //指定TaskExecutor 的bean name
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setCorePoolSize(10);
        threadPoolTaskExecutor.setMaxPoolSize(20);
        threadPoolTaskExecutor.setQueueCapacity(1000);
        threadPoolTaskExecutor.setThreadNamePrefix("async-t-");
        return threadPoolTaskExecutor;
    }
 
    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setThreadNamePrefix("p-scheduler-");
        scheduler.setPoolSize(10);
        return scheduler;
    }
 
}

2.2 配置需要開啟多線程的method

@Async("asyncExecutor") //指定TaskExecutor 的bean name
public void refreshOnlineUser(OAuth2Authentication auth2Authentication){...}
 
@Scheduled(cron = "0/20 * * * * ?") //每隔20秒執(zhí)行一次
public void count() {...}

2.3 測試

請注意“ThreadNamePrefix”,如果打印出來的日志里,thread name沒有預(yù)設(shè)的前綴,那么,配置的這個bean TaskExecutor 或 TaskScheduler 就沒有生效(這兩個線程的前綴不同

此時,請檢查@Async、@Scheduled。不需要對比所有的配置文件,最快的解決方案:

  • 配置TaskExecutor 或 TaskScheduler 時,設(shè)置bean name;
  • 同時,指定@Async、@Scheduled使用的bean name

3 異常處理

異步任務(wù)的異常,不會讓主線程停止運行。(應(yīng)該的,本來就不在一個線程里

當然,凡事都有例外,比如,分布式事務(wù)的“其中一種機制”補償機制,與這種情況類似。當異步任務(wù)拋異常后,通知main Thread使用“補償措施”回滾事務(wù)。

請參考http://blog.csdn.net/blueheart20/article/details/44648667

目前,還沒有這樣的需求,所以,不建議使用這種“重型”的解決方案

4 補充描述

4.1 線程的名字沒有預(yù)設(shè)的前綴

首先,能看到線程的名字,說明在方法上添加的注解生效了;其次,預(yù)定的前綴沒有打印出來,那就說明bean的配置,沒有生效。

一句話,spring根據(jù)方法上的注解,使用默認的實現(xiàn)類創(chuàng)建了線程,沒有使用配置的Thread pool。

解決方案:

  • 設(shè)置bean的name。eg.
@Bean("asyncTask")
public TaskExecutor taskExecutor() 
  • 指定方法使用的bean name。 eg.
@Async("asyncTask")
public void hello() 

4.2 什么樣的class、method可以使用@Async、@Scheduled

開啟異步任務(wù)、定時任務(wù),只跟method有關(guān)。每次調(diào)用這些method的時候,spring上下文都會開啟新的線程。

所以,任何一個method都可以添加@Async、@Scheduled。
注意:這兩個注解必須放在實現(xiàn)類的方法上,如果,放在interface的方法聲明上,不會生效的。
錯誤示例:

public interface UserLoginHistoryService {
    //每晚12點清理日志 --- 這個定時任務(wù)不會執(zhí)行的,因為,標記在interface的方法聲明上了
    @Scheduled(cron = "0 0 0 * * ?")
    void deleteHistory();
}

4.3 為什么必須指定TaskExecutor的bean name?

網(wǎng)上可以找到很多帖子,說是@Async沒有生效。據(jù)說是有其他的配置覆蓋了(沒找到...)。所以,我猜測,應(yīng)該是spring的BUG。
解決辦法:指定TaskExecutor 的name

4.4 @EnableAsync、@EnableScheduling

這兩個注解是用來通知spring 容器,嘗試加載相關(guān)的bean,啟用異步任務(wù)或定時任務(wù)。所以,一個項目里,只需要在任意一個@Configuaration標記的class上,添加這兩個注解即可。

當然,規(guī)范些,還是在配置bean TaskExecutor 、TaskScheduler 的class上,添加@EnableAsync 、@EnableScheduling

注:不能濫用注解。雖然,多次配置,也不會報錯,但,多余的配置,會造成誤解。

4.5 當預(yù)設(shè)的線程用完了

線程池中配置的線程是有數(shù)的,當用完了,程序或者是等待,或者,不處理。死活都要撐著,只能讓服務(wù)器崩潰。這種情況,主要針對的是TaskExecutor (通常,一個項目中TaskScheduler 定時任務(wù)是有限的)。

1、允許等待的線程,本身處理的任務(wù),耗時要少些。再配合QueueCapacity隊列,可以最大限度地保障系統(tǒng)高效地運行(能處理的任務(wù),快速處理完,然后,歸還線程;不能處理的任務(wù),先排隊,輪到了,再處理);

2、直接拒絕的線程,應(yīng)該是那些本身就要耗時很長,超出服務(wù)器處理能力的請求?;蛘呔芙^,或者,多加幾臺服務(wù)器

當然,也可以適當增加線程的數(shù)量,這個,得考慮硬件條件

4.6 守護線程

web應(yīng)用跑在JVM上,線程也跑在JVM上,嚴格說起來,咱們new Thread()跟web應(yīng)用沒關(guān)系。換句話說,關(guān)閉web應(yīng)用后,某些線程還在繼續(xù)運行。
如果你使用我的配置方案啟用Async/Schedule,spring容器會自動管理這些線程。當web應(yīng)用關(guān)閉后,相關(guān)的線程也會stop。

詳細的原理,請查閱“守護線程”

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,657評論 19 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,282評論 6 342
  • 博客原文 徒手翻譯spring framework 4.2.3官方文檔的第33章,若有翻譯不當之處請指正。 定時任...
    rabbitGYK閱讀 5,852評論 4 24
  • 前段時間遇到這樣一個問題,有人問微信朋友圈的上傳圖片的功能怎么做才能讓用戶的等待時間較短,比如說一下上傳9張圖片,...
    加油碼農(nóng)閱讀 1,285評論 0 2
  • 黃葉從暮秋一下子落入初冬 就像我在廣場拐進了胡同 黃葉只代表了秋天的重量 遇到雪就能幻化成另一顆魂靈 廣場明亮,只...
    甘肅子溪閱讀 454評論 4 4

友情鏈接更多精彩內(nèi)容