Spring定時(shí)任務(wù)@scheduled多線程使用@Async注解示例
1.開篇
在Spring定時(shí)任務(wù)@Scheduled注解使用方式淺窺這篇文章里面提及過,spring的定時(shí)任務(wù)默認(rèn)是單線程的,他在某些場(chǎng)景下會(huì)造成堵塞,那么如果我們想讓每一個(gè)任務(wù)都起一條線程去執(zhí)行呢?
2.使用@Async
我們可以使用Spring的@Async注解十分容易的實(shí)現(xiàn)多線程的任務(wù)執(zhí)行。
測(cè)試代碼:
@Scheduled(cron = "0/2 * * * * ?") @Async public void doTask() throws InterruptedException { logger.info(Thread.currentThread().getName()+"===task run"); Thread.sleep(6*1_000); logger.info(Thread.currentThread().getName()+"===task end"); }
結(jié)果日志
2018-06-12 16:02:42.005 [taskExecutor-97] INFO service.task.testTask -taskExecutor-97===task run
2018-06-12 16:02:42.007 [taskExecutor-94] INFO service.task.testTask -taskExecutor-94===task end
2018-06-12 16:02:44.004 [taskExecutor-98] INFO service.task.testTask -taskExecutor-98===task run
2018-06-12 16:02:44.015 [taskExecutor-95] INFO service.task.testTask -taskExecutor-95===task end
2018-06-12 16:02:46.004 [taskExecutor-99] INFO service.task.testTask -taskExecutor-99===task run
2018-06-12 16:02:46.014 [taskExecutor-96] INFO service.task.testTask -taskExecutor-96===task end
2018-06-12 16:02:48.004 [taskExecutor-100] INFO service.task.testTask -taskExecutor-100===task run
2018-06-12 16:02:48.010 [taskExecutor-97] INFO service.task.testTask -taskExecutor-97===task end
2018-06-12 16:02:50.005 [taskExecutor-1] INFO service.task.testTask -taskExecutor-1===task run
2018-06-12 16:02:50.008 [taskExecutor-98] INFO service.task.testTask -taskExecutor-98===task end
2018-06-12 16:02:52.006 [taskExecutor-2] INFO service.task.testTask -taskExecutor-2===task run
截取部分結(jié)果日志我們可以看到,在上一個(gè)任務(wù)6s的執(zhí)行時(shí)間內(nèi),下一個(gè)任務(wù)并沒有等待上一個(gè)任務(wù)結(jié)束,而是在任務(wù)開始時(shí)間直接開啟了一條新的線程進(jìn)行執(zhí)行。
仔細(xì)觀察結(jié)果我們還可以發(fā)現(xiàn),每條結(jié)果都是一條新的線程,直到100時(shí),才又從第一條線程開始。這是因?yàn)樵谀J(rèn)不做配置的情況下,@Async所使用的線程池容量為100,每次需要的時(shí)候都會(huì)從中拿出一條,直到用完,才會(huì)等待之前的線程釋放,不會(huì)再自己擴(kuò)容。
下面我們稍稍改下代碼來證實(shí)一下:
@Scheduled(cron = "0/2 * * * * ?") @Async public void doTask() throws InterruptedException { logger.info(Thread.currentThread().getName()+"===task run"); Thread.sleep(300*1_000); logger.info(Thread.currentThread().getName()+"===task end"); }
這次我讓任務(wù)執(zhí)行的時(shí)間等于300s,大于100條線程總間隔時(shí)間來耗盡線程池中的線程。
結(jié)果日志
2018-06-12 16:26:44.411 [taskExecutor-93] INFO service.task.testTask -taskExecutor-93===task run
2018-06-12 16:26:46.853 [taskExecutor-94] INFO service.task.testTask -taskExecutor-94===task run
2018-06-12 16:26:48.008 [taskExecutor-95] INFO service.task.testTask -taskExecutor-95===task run
2018-06-12 16:26:50.008 [taskExecutor-96] INFO service.task.testTask -taskExecutor-96===task run
2018-06-12 16:26:52.006 [taskExecutor-97] INFO service.task.testTask -taskExecutor-97===task run
2018-06-12 16:26:54.008 [taskExecutor-98] INFO service.task.testTask -taskExecutor-98===task run
2018-06-12 16:26:56.006 [taskExecutor-99] INFO service.task.testTask -taskExecutor-99===task run
2018-06-12 16:26:58.005 [taskExecutor-100] INFO service.task.testTask -taskExecutor-100===task run2018-06-12 16:28:40.142 [taskExecutor-1] INFO service.task.testTask -taskExecutor-1===task end
2018-06-12 16:28:40.149 [taskExecutor-1] INFO service.task.testTask -taskExecutor-1===task run
2018-06-12 16:28:42.117 [taskExecutor-2] INFO service.task.testTask -taskExecutor-2===task end
2018-06-12 16:28:42.121 [taskExecutor-2] INFO service.task.testTask -taskExecutor-2===task run
2018-06-12 16:28:44.253 [taskExecutor-3] INFO service.task.testTask -taskExecutor-3===task end
2018-06-12 16:28:44.257 [taskExecutor-3] INFO service.task.testTask -taskExecutor-3===task run
2018-06-12 16:28:46.027 [taskExecutor-4] INFO service.task.testTask -taskExecutor-4===task end
2018-06-12 16:28:46.031 [taskExecutor-4] INFO service.task.testTask -taskExecutor-4===task run
通過日志我們可以看到,再第100條線程也開始執(zhí)行任務(wù)后,沒有新的線程再被創(chuàng)建,而是等待有線程執(zhí)行完后,再開始執(zhí)行本次任務(wù)。
3.配置線程池大小
雖然上面的方式已經(jīng)解決了我們的問題,但是總覺得不太好,有時(shí)候我們需要異步執(zhí)行任務(wù),但是又不需要這么多的線程的時(shí)候,我們可以使用下面的配置來設(shè)置線程池的大小
配置文件:
<task:annotation-driven scheduler="testScheduler" /> <!-- 配置任務(wù)線程池和線程池大小 --> <task:scheduler id="testScheduler" pool-size="6" />
測(cè)試代碼:
@Scheduled(cron = "0/2 * * * * ?") @Async public void doTask() throws InterruptedException { logger.info(Thread.currentThread().getName()+"===task run"); Thread.sleep(6*1_000); logger.info(Thread.currentThread().getName()+"===task end"); }
結(jié)果日志:
2018-06-12 18:32:56.032 [taskExecutor-1] INFO service.task.testTask -taskExecutor-1===task run
2018-06-12 18:32:58.007 [taskExecutor-2] INFO service.task.testTask -taskExecutor-2===task run
2018-06-12 18:33:00.005 [taskExecutor-3] INFO service.task.testTask -taskExecutor-3===task run
2018-06-12 18:33:02.008 [taskExecutor-4] INFO service.task.testTask -taskExecutor-4===task run
2018-06-12 18:33:02.036 [taskExecutor-1] INFO service.task.testTask -taskExecutor-1===task end
2018-06-12 18:33:04.327 [taskExecutor-2] INFO service.task.testTask -taskExecutor-2===task end
2018-06-12 18:33:04.328 [taskExecutor-5] INFO service.task.testTask -taskExecutor-5===task run
2018-06-12 18:33:06.007 [taskExecutor-6] INFO service.task.testTask -taskExecutor-6===task run
2018-06-12 18:33:06.010 [taskExecutor-3] INFO service.task.testTask -taskExecutor-3===task end
2018-06-12 18:33:08.459 [taskExecutor-4] INFO service.task.testTask -taskExecutor-4===task end
2018-06-12 18:33:08.460 [taskExecutor-7] INFO service.task.testTask -taskExecutor-7===task run
2018-06-12 18:33:10.011 [taskExecutor-8] INFO service.task.testTask -taskExecutor-8===task run
2018-06-12 18:33:10.332 [taskExecutor-5] INFO service.task.testTask -taskExecutor-5===task end
2018-06-12 18:33:12.005 [taskExecutor-9] INFO service.task.testTask -taskExecutor-9===task run
2018-06-12 18:33:12.012 [taskExecutor-6] INFO service.task.testTask -taskExecutor-6===task end
2018-06-12 18:33:14.904 [taskExecutor-10] INFO service.task.testTask -taskExecutor-10===task run
2018-06-12 18:33:14.904 [taskExecutor-7] INFO service.task.testTask -taskExecutor-7===task end
結(jié)果和我們預(yù)料的并不一樣啊,線程數(shù)超過了6,這是什么原因呢?
其實(shí)如果我們?cè)谑褂聾Async時(shí)想使用配置好的線程池,需要為@Async注解添加value屬性來制定所用的線程池。
修改后的代碼
@Scheduled(cron = "0/2 * * * * ?") @Async("testScheduler") public void doTask() throws InterruptedException { logger.info(Thread.currentThread().getName()+"===task run"); Thread.sleep(30*1_000); logger.info(Thread.currentThread().getName()+"===task end"); }
結(jié)果日志:
2018-06-12 18:54:42.035 [testScheduler-3] INFO service.task.testTask -testScheduler-3===task run
2018-06-12 18:54:44.010 [testScheduler-2] INFO service.task.testTask -testScheduler-2===task run
2018-06-12 18:54:46.007 [testScheduler-1] INFO service.task.testTask -testScheduler-1===task run
2018-06-12 18:54:48.007 [testScheduler-5] INFO service.task.testTask -testScheduler-5===task run
2018-06-12 18:54:50.005 [testScheduler-4] INFO service.task.testTask -testScheduler-4===task run
2018-06-12 18:54:52.021 [testScheduler-6] INFO service.task.testTask -testScheduler-6===task run2018-06-12 18:55:12.039 [testScheduler-3] INFO service.task.testTask -testScheduler-3===task end
2018-06-12 18:55:12.044 [testScheduler-3] INFO service.task.testTask -testScheduler-3===task run
2018-06-12 18:55:14.016 [testScheduler-2] INFO service.task.testTask -testScheduler-2===task end
2018-06-12 18:55:14.022 [testScheduler-2] INFO service.task.testTask -testScheduler-2===task run
2018-06-12 18:55:16.289 [testScheduler-1] INFO service.task.testTask -testScheduler-1===task end
2018-06-12 18:55:16.297 [testScheduler-1] INFO service.task.testTask -testScheduler-1===task run
2018-06-12 18:55:18.289 [testScheduler-5] INFO service.task.testTask -testScheduler-5===task end
2018-06-12 18:55:18.293 [testScheduler-5] INFO service.task.testTask -testScheduler-5===task run
2018-06-12 18:55:20.009 [testScheduler-4] INFO service.task.testTask -testScheduler-4===task end
2018-06-12 18:55:20.014 [testScheduler-4] INFO service.task.testTask -testScheduler-4===task run
2018-06-12 18:55:22.165 [testScheduler-6] INFO service.task.testTask -testScheduler-6===task end
2018-06-12 18:55:22.172 [testScheduler-6] INFO service.task.testTask -testScheduler-6===task run
這次結(jié)果和我們的預(yù)計(jì)是一樣的了,在初始的6條線程使用完畢以后就會(huì)等待之前的線程釋放啦,同時(shí)也可以看到線程池名是我們?cè)O(shè)置的線程池。
以上就是Spring定時(shí)任務(wù)@scheduled多線程使用@Async注解示例的詳細(xì)內(nèi)容,更多關(guān)于Spring @scheduled使用@Async的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Spring自帶定時(shí)任務(wù)@Scheduled注解實(shí)例講解
- Spring定時(shí)任務(wù)@Scheduled注解(cron表達(dá)式fixedRate?fixedDelay)
- Spring中的@Scheduled定時(shí)任務(wù)注解詳解
- SpringBoot中@Scheduled()注解以及cron表達(dá)式詳解
- Spring 定時(shí)任務(wù)@Scheduled 注解中的 Cron 表達(dá)式詳解
- SpringBoot中定時(shí)任務(wù)@Scheduled注解的使用解讀
- spring-boot通過@Scheduled配置定時(shí)任務(wù)及定時(shí)任務(wù)@Scheduled注解的方法
- 詳解在Spring3中使用注解(@Scheduled)創(chuàng)建計(jì)劃任務(wù)
- spring @Scheduled定時(shí)任務(wù)注解使用方法及注意事項(xiàng)小結(jié)
相關(guān)文章
玩轉(zhuǎn)SpringBoot2快速整合攔截器的方法
這篇文章主要介紹了玩轉(zhuǎn)SpringBoot2快速整合攔截器的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09Intellij Idea修改代碼方法參數(shù)自動(dòng)提示快捷鍵的操作
這篇文章主要介紹了Intellij Idea修改代碼方法參數(shù)自動(dòng)提示快捷鍵的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-01-01springboot獲取真實(shí)ip地址的方法實(shí)例
在使用springboot時(shí),需要獲取訪問客戶端的IP地址,所以下面這篇文章主要給大家介紹了關(guān)于springboot獲取真實(shí)ip地址的相關(guān)資料,需要的朋友可以參考下2022-06-06Java多線程編程之使用Exchanger數(shù)據(jù)交換實(shí)例
這篇文章主要介紹了Java多線程編程之使用Exchanger數(shù)據(jù)交換實(shí)例,本文直接給出實(shí)例代碼,需要的朋友可以參考下2015-05-05Java數(shù)據(jù)結(jié)構(gòu)之紅黑樹的實(shí)現(xiàn)方法和原理詳解
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)之紅黑樹的實(shí)現(xiàn)方法和原理,紅黑樹是一種特殊的二叉查找樹,每個(gè)結(jié)點(diǎn)都要儲(chǔ)存位表示結(jié)點(diǎn)的顏色,或紅或黑,本文將通過示例為大家詳細(xì)講講紅黑樹的原理及實(shí)現(xiàn),感興趣的朋友可以了解一下2024-02-02Spring?Boot的幾種統(tǒng)一處理方式梳理小結(jié)
這篇文章主要為大家介紹了Spring?Boot的幾種統(tǒng)一處理方式梳理小結(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05