Java調(diào)度線程池ScheduledThreadPoolExecutor不執(zhí)行問(wèn)題分析
前言
最近在調(diào)試一個(gè)監(jiān)控應(yīng)用指標(biāo)的時(shí)候發(fā)現(xiàn)定時(shí)器在服務(wù)啟動(dòng)執(zhí)行一次之后就不執(zhí)行了,這里用的定時(shí)器是Java的調(diào)度線程池ScheduledThreadPoolExecutor,后來(lái)經(jīng)過(guò)排查發(fā)現(xiàn)ScheduledThreadPoolExecutor線程池處理任務(wù)如果拋出異常,會(huì)導(dǎo)致線程池不調(diào)度;下面就通過(guò)一個(gè)例子簡(jiǎn)單分析下為什么異常會(huì)導(dǎo)致ScheduledThreadPoolExecutor不執(zhí)行。
ScheduledThreadPoolExecutor不調(diào)度分析
示例程序
在示例程序可以看到當(dāng)計(jì)數(shù)器中的計(jì)數(shù)達(dá)到5的時(shí)候就會(huì)主動(dòng)拋出一個(gè)異常,拋出異常后ScheduledThreadPoolExecutor就不調(diào)度了。
public class ScheduledTask {
private static final AtomicInteger count = new AtomicInteger(0);
private static final ScheduledThreadPoolExecutor SCHEDULED_TASK = new ScheduledThreadPoolExecutor(
1, new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(Thread.currentThread().getThreadGroup(), r, "sc-task");
t.setDaemon(true);
return t;
}
});
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
SCHEDULED_TASK.scheduleWithFixedDelay(() -> {
System.out.println(111);
if (count.get() == 5) {
throw new IllegalArgumentException("my exception");
}
count.incrementAndGet();
}, 0, 5, TimeUnit.SECONDS);
latch.await();
}
}
源碼分析
ScheduledThreadPoolExecutor#run
run方法內(nèi)部首先判斷任務(wù)是不是周期性的任務(wù),如果不是周期性任務(wù)通過(guò)ScheduledFutureTask.super.run();執(zhí)行任務(wù);如果狀態(tài)是運(yùn)行中或shutdown,取消任務(wù)執(zhí)行;如果是周期性的任務(wù),通過(guò)ScheduledFutureTask.super.runAndReset()執(zhí)行任務(wù)并且重新設(shè)置狀態(tài),成功了就會(huì)執(zhí)行setNextRunTime設(shè)置下次調(diào)度的時(shí)間,問(wèn)題就是出現(xiàn)在ScheduledFutureTask.super.runAndReset(),這里執(zhí)行任務(wù)出現(xiàn)了異常,導(dǎo)致結(jié)果為false,就不進(jìn)行下次調(diào)度時(shí)間設(shè)置等
public void run() {
boolean periodic = isPeriodic();
if (!canRunInCurrentRunState(periodic))
cancel(false);
else if (!periodic)
ScheduledFutureTask.super.run();
else if (ScheduledFutureTask.super.runAndReset()) {
setNextRunTime();
reExecutePeriodic(outerTask);
}
}
*FutureTask#runAndReset
在線程任務(wù)執(zhí)行過(guò)程中拋出異常,然后catch到了異常,最終導(dǎo)致這個(gè)方法返回false,然后ScheduledThreadPoolExecutor#run就不設(shè)置下次執(zhí)行時(shí)間了,代碼c.call(); 拋出異常,跳過(guò)ran = true;代碼,最終runAndReset返回false。
protected boolean runAndReset() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return false;
boolean ran = false;
int s = state;
try {
Callable<V> c = callable;
if (c != null && s == NEW) {
try {
c.call(); // don't set result
ran = true;
} catch (Throwable ex) {
setException(ex);
}
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
return ran && s == NEW;
}
總結(jié)
Java的ScheduledThreadPoolExecutor定時(shí)任務(wù)線程池所調(diào)度的任務(wù)中如果拋出了異常,并且異常沒(méi)有捕獲直接拋到框架中,會(huì)導(dǎo)致ScheduledThreadPoolExecutor定時(shí)任務(wù)不調(diào)度了,具體是因?yàn)楫?dāng)異常拋到ScheduledThreadPoolExecutor框架中時(shí)不進(jìn)行下次調(diào)度時(shí)間的設(shè)置,從而導(dǎo)致ScheduledThreadPoolExecutor定時(shí)任務(wù)不調(diào)度。
到此這篇關(guān)于Java調(diào)度線程池ScheduledThreadPoolExecutor不執(zhí)行問(wèn)題分析的文章就介紹到這了,更多相關(guān)Java ScheduledThreadPoolExecutor內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java自帶定時(shí)任務(wù)ScheduledThreadPoolExecutor實(shí)現(xiàn)定時(shí)器和延時(shí)加載功能
- java高并發(fā)ScheduledThreadPoolExecutor類深度解析
- 詳解Java ScheduledThreadPoolExecutor的踩坑與解決方法
- java高并發(fā)ScheduledThreadPoolExecutor與Timer區(qū)別
- java 定時(shí)器線程池(ScheduledThreadPoolExecutor)的實(shí)現(xiàn)
- Java使用quartz實(shí)現(xiàn)定時(shí)任務(wù)示例詳解
- Java實(shí)現(xiàn)定時(shí)任務(wù)最簡(jiǎn)單的3種方法
- Java項(xiàng)目實(shí)現(xiàn)定時(shí)任務(wù)的三種方法
- Java定時(shí)任務(wù)ScheduledThreadPoolExecutor示例詳解
相關(guān)文章
詳解eclipse中Maven工程使用Tomcat7以上插件的方法
本篇文章主要介紹了詳解eclipse中Maven工程使用Tomcat7以上插件的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-12-12
解決springboot啟動(dòng)報(bào)錯(cuò)bean找不到的問(wèn)題
這篇文章主要介紹了解決springboot啟動(dòng)報(bào)錯(cuò)bean找不到原因,本文給大家分享完美解決方案,通過(guò)圖文相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03
Springboot?整合maven插口調(diào)用maven?release?plugin實(shí)現(xiàn)一鍵打包功能
這篇文章主要介紹了Springboot?整合maven插口調(diào)用maven?release?plugin實(shí)現(xiàn)一鍵打包功能,整合maven-invoker使程序去執(zhí)行mvn命令,結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-03-03
MyBatis-Plus 與Druid 數(shù)據(jù)源操作
SpringBoot框架集成MyBatis-Plus和Druid數(shù)據(jù)源,簡(jiǎn)化了數(shù)據(jù)操作與監(jiān)控,MyBatis-Plus作為MyBatis的增強(qiáng)工具,自動(dòng)實(shí)現(xiàn)CRUD操作,減少手寫(xiě)SQL,提供分頁(yè)、邏輯刪除等功能,本文介紹MyBatis-Plus & Druid 數(shù)據(jù)源總結(jié),感興趣的朋友一起看看吧2024-09-09
Springdoc替換swagger的實(shí)現(xiàn)步驟分解
最近在spring看到的,spring要對(duì)api文檔動(dòng)手了,有些人說(shuō)swagger不好用,其實(shí)也沒(méi)那么不好用,有人說(shuō)代碼還是有點(diǎn)侵入性,這倒是真的,我剛試了springdoc可以說(shuō)還是有侵入性但是也可以沒(méi)有侵入性,這就看你對(duì)文檔有什么要求了2023-02-02
Java使用JSONObject操作json實(shí)例解析
這篇文章主要介紹了Java使用JSONObject操作json,結(jié)合實(shí)例形式較為詳細(xì)的分析了Java使用JSONObject解析json數(shù)據(jù)相關(guān)原理、使用技巧與操作注意事項(xiàng),需要的朋友可以參考下2020-04-04
Spring Security+JWT實(shí)現(xiàn)認(rèn)證與授權(quán)的實(shí)現(xiàn)
本文主要介紹了Spring Security+JWT實(shí)現(xiàn)認(rèn)證與授權(quán)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04

