Java調(diào)度線程池ScheduledThreadPoolExecutor不執(zhí)行問題分析
前言
最近在調(diào)試一個監(jiān)控應(yīng)用指標(biāo)的時候發(fā)現(xiàn)定時器在服務(wù)啟動執(zhí)行一次之后就不執(zhí)行了,這里用的定時器是Java的調(diào)度線程池ScheduledThreadPoolExecutor
,后來經(jīng)過排查發(fā)現(xiàn)ScheduledThreadPoolExecutor
線程池處理任務(wù)如果拋出異常,會導(dǎo)致線程池不調(diào)度;下面就通過一個例子簡單分析下為什么異常會導(dǎo)致ScheduledThreadPoolExecutor
不執(zhí)行。
ScheduledThreadPoolExecutor不調(diào)度分析
示例程序
在示例程序可以看到當(dāng)計數(shù)器中的計數(shù)達到5的時候就會主動拋出一個異常,拋出異常后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ù)通過ScheduledFutureTask.super.run();
執(zhí)行任務(wù);如果狀態(tài)是運行中或shutdown,取消任務(wù)執(zhí)行;如果是周期性的任務(wù),通過ScheduledFutureTask.super.runAndReset()
執(zhí)行任務(wù)并且重新設(shè)置狀態(tài),成功了就會執(zhí)行setNextRunTime
設(shè)置下次調(diào)度的時間,問題就是出現(xiàn)在ScheduledFutureTask.super.runAndReset()
,這里執(zhí)行任務(wù)出現(xiàn)了異常,導(dǎo)致結(jié)果為false,就不進行下次調(diào)度時間設(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í)行過程中拋出異常,然后catch
到了異常,最終導(dǎo)致這個方法返回false,然后ScheduledThreadPoolExecutor#run
就不設(shè)置下次執(zhí)行時間了,代碼c.call();
拋出異常,跳過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
定時任務(wù)線程池所調(diào)度的任務(wù)中如果拋出了異常,并且異常沒有捕獲直接拋到框架中,會導(dǎo)致ScheduledThreadPoolExecutor
定時任務(wù)不調(diào)度了,具體是因為當(dāng)異常拋到ScheduledThreadPoolExecutor
框架中時不進行下次調(diào)度時間的設(shè)置,從而導(dǎo)致ScheduledThreadPoolExecutor
定時任務(wù)不調(diào)度。
到此這篇關(guān)于Java調(diào)度線程池ScheduledThreadPoolExecutor不執(zhí)行問題分析的文章就介紹到這了,更多相關(guān)Java ScheduledThreadPoolExecutor內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java自帶定時任務(wù)ScheduledThreadPoolExecutor實現(xiàn)定時器和延時加載功能
- java高并發(fā)ScheduledThreadPoolExecutor類深度解析
- 詳解Java ScheduledThreadPoolExecutor的踩坑與解決方法
- java高并發(fā)ScheduledThreadPoolExecutor與Timer區(qū)別
- java 定時器線程池(ScheduledThreadPoolExecutor)的實現(xiàn)
- Java使用quartz實現(xiàn)定時任務(wù)示例詳解
- Java實現(xiàn)定時任務(wù)最簡單的3種方法
- Java項目實現(xiàn)定時任務(wù)的三種方法
- Java定時任務(wù)ScheduledThreadPoolExecutor示例詳解
相關(guān)文章
Java數(shù)據(jù)結(jié)構(gòu)之鏈表(動力節(jié)點之Java學(xué)院整理)
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)之鏈表(動力節(jié)點之Java學(xué)院整理)的相關(guān)資料,需要的朋友可以參考下2017-04-04Springboot中如何使用過濾器校驗PSOT類型請求參數(shù)內(nèi)容
在Springboot中創(chuàng)建過濾器,用來過濾所有POST類型請求并獲取body中的參數(shù)進行校驗內(nèi)容是否合法,該方法僅適用于POST類型請求,本文給大家介紹Springboot中如何使用過濾器校驗PSOT類型請求參數(shù)內(nèi)容,感興趣的朋友一起看看吧2023-08-08SpringBoot中@EnableAsync和@Async注解的使用小結(jié)
在SpringBoot中,可以通過@EnableAsync注解來啟動異步方法調(diào)用的支持,通過@Async注解來標(biāo)識異步方法,讓方法能夠在異步線程中執(zhí)行,本文就來介紹一下,感興趣的可以了解一下2023-11-11Java中使用BeanMap將對象轉(zhuǎn)為Map詳解
這篇文章主要介紹了Java中使用BeanMap將對象轉(zhuǎn)為Map詳解,BeanMap?是?Apache?Commons?BeanUtils?庫中的一個類,BeanMap?可以將?Java?對象的屬性作為鍵,屬性值作為對應(yīng)的值,存儲在一個?Map?中,它提供了一種將?Java?對象轉(zhuǎn)換為?Map?的方式,需要的朋友可以參考下2024-01-01SpringBoot實現(xiàn)數(shù)據(jù)加密脫敏的示例代碼
這篇文章主要為大家學(xué)習(xí)介紹了SpringBoot如何利用注解+反射+AOP實現(xiàn)數(shù)據(jù)加密脫敏的功能,文中的示例代碼講解詳細,需要的可以參考一下2023-08-08如何利用SpringBoot搭建WebService服務(wù)接口
之前項目經(jīng)理想要開發(fā)一個webservice的協(xié)議,給我一個星期的時間,后面用springboot開發(fā)了webservice,這篇文章主要給大家介紹了關(guān)于如何利用SpringBoot搭建WebService服務(wù)接口的相關(guān)資料,需要的朋友可以參考下2023-11-11