欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關(guān)于ScheduledThreadPoolExecutor不執(zhí)行的原因分析

 更新時間:2023年08月10日 14:46:32   作者:Redick01  
這篇文章主要介紹了關(guān)于ScheduledThreadPoolExecutor不執(zhí)行的原因分析,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

ScheduledThreadPoolExecutor不執(zhí)行原因分析

最近在調(diào)試一個監(jiān)控應(yīng)用指標的時候發(fā)現(xiàn)定時器在服務(wù)啟動執(zhí)行一次之后就不執(zhí)行了,這里用的定時器是Java的調(diào)度線程池 ScheduledThreadPoolExecutor ,后來經(jīng)過排查發(fā)現(xiàn) ScheduledThreadPoolExecutor 線程池處理任務(wù)如果拋出異常,會導致線程池不調(diào)度;下面就通過一個例子簡單分析下為什么異常會導致 ScheduledThreadPoolExecutor 不執(zhí)行。

ScheduledThreadPoolExecutor不調(diào)度分析

示例程序

在示例程序可以看到當計數(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)了異常,導致結(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 到了異常,最終導致這個方法返回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;
    }

注意:

Java ScheduledThreadPoolExecutor 定時任務(wù)線程池所調(diào)度的任務(wù)中如果拋出了異常,并且異常沒有捕獲直接拋到框架中,會導致 ScheduledThreadPoolExecutor 定時任務(wù)不調(diào)度了,具體是因為當異常拋到 ScheduledThreadPoolExecutor 框架中時不進行下次調(diào)度時間的設(shè)置,從而導致 ScheduledThreadPoolExecutor 定時任務(wù)不調(diào)度。

ScheduledThreadPoolExecutor線程池例子

ScheduledThreadPoolExecutor 使用

ScheduledThreadPoolExecutor 繼承自 ThreadPoolExecutor,主要用來給定時間運行任務(wù),或者定期執(zhí)行任務(wù)。

在 ScheduledThreadPoolExecutor 的實現(xiàn)中,使用了 FutureTask 運行任務(wù)以及使用無界隊列 DelayedWorkQueue 來保存任務(wù)。

1. 使用示例

  • 提交任務(wù)

ScheduledThreadPoolExecutor 實現(xiàn)了 ScheduledExecutorService 接口,其中,接口中有四個需要實現(xiàn)的方法,其中 schedule() 的兩個方法需要設(shè)置任務(wù)以及任務(wù)啟動的延遲時間,scheduleAtFixedRate() 可以設(shè)置任務(wù)定時重復(fù)執(zhí)行,scheduleWithFixedDelay() 則是設(shè)置兩個任務(wù)之間的執(zhí)行延遲時間。

ScheduledThreadPoolExecutor poolExecutor = new ScheduledThreadPoolExecutor(2); ?
poolExecutor.schedule(() -> { ?
?? ?// 提交的任務(wù)
}, 5, TimeUnit.HOURS); ?
poolExecutor.scheduleAtFixedRate(() -> { ?
?? ?// 提交的任務(wù)
}, 0, 5, TimeUnit.HOURS); ?
poolExecutor.scheduleWithFixedDelay(() -> { ?
?? ?// 提交的任務(wù)
}, 0, 5, TimeUnit.HOURS);
  • 簡單例子

下面的例子中每 500 毫秒打印一次字符串,executor 會有 5 秒的時間來等待任務(wù)執(zhí)行結(jié)束,也就是一共可以打印 10 次字符串。

ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(10); ?
executor.scheduleWithFixedDelay(() -> { ?
? ? System.out.println("測試"); ?
}, 0, 500, TimeUnit.MILLISECONDS); ?
try { ?
? ? executor.awaitTermination(5, TimeUnit.SECONDS); ?
? ? executor.shutdown(); ?
} catch (InterruptedException e) { ?
? ? e.printStackTrace(); ?
}

ScheduledThreadPoolExecutor 原理

1. DelayedWorkQueue

ScheduledThreadPoolExecutor 的構(gòu)造方法對 DelayedWorkQueue 進行了初始化,并且最大線程數(shù)量設(shè)置成了 Integer.MAX_VALUE。

public ScheduledThreadPoolExecutor(int corePoolSize) { ?
? ? super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, ?
? ? ? ? ? new DelayedWorkQueue()); ?
}

其中,DelayedWorkQueue 中的隊列是 RunnableScheduledFuture 類型以及容量為 16 的數(shù)組。

private static final int INITIAL_CAPACITY = 16; ?
private RunnableScheduledFuture<?>[] queue = ?
? ? new RunnableScheduledFuture<?>[INITIAL_CAPACITY]; ?
private final ReentrantLock lock = new ReentrantLock();

隊列添加任務(wù)是以 DelayedWorkQueue 以堆作為數(shù)據(jù)結(jié)構(gòu)存儲任務(wù),在添加元素的時候,會使用基于 Siftup 版本進行元素添加,并且會根據(jù)任務(wù)的執(zhí)行時間的大小來排序。

public boolean offer(Runnable x) { ?
? ? if (x == null) ?
? ? ? ? throw new NullPointerException();
? ? RunnableScheduledFuture<?> e = (RunnableScheduledFuture<?>)x; ?
? ? final ReentrantLock lock = this.lock; ?
? ? lock.lock();
? ? try {
?? ? ? ?// 當隊列的容量不夠,會擴充 50%
? ? ? ? int i = size;
? ? ? ? if (i >= queue.length)
? ? ? ? ? ? grow();
? ? ? ? size = i + 1;
? ? ? ? if (i == 0) {
? ? ? ? ? ? queue[0] = e;
? ? ? ? ? ? setIndex(e, 0);
? ? ? ? } else { ?
? ? ? ? ? ? siftUp(i, e); ?
? ? ? ? } ?
? ? ? ? if (queue[0] == e) { ?
? ? ? ? ? ? leader = null; ?
? ? ? ? ? ? available.signal(); ?
? ? ? ? } ?
? ? } finally { ?
? ? ? ? lock.unlock(); ?
? ? } ?
? ? return true; ?
}

2. delayedExecute()

ScheduledExecutorService 接口的四個實現(xiàn)方法中都涉及到了 delayedExecute(),方法主要用來判斷線程池的狀態(tài)以及對線程進行初始化。

private void delayedExecute(RunnableScheduledFuture<?> task) {
?? ?// 如果線程池關(guān)閉了,需要執(zhí)行飽和策略
? ? if (isShutdown()) ?
? ? ? ? reject(task); ?
? ? else {
?? ? ? ?// 添加到隊列中
? ? ? ? super.getQueue().add(task); ?
? ? ? ? if (isShutdown() && ?
? ? ? ? ? ? !canRunInCurrentRunState(task.isPeriodic()) && ?
? ? ? ? ? ? remove(task)) ?
? ? ? ? ? ? task.cancel(false); ?
? ? ? ? else
?? ? ? ? ? ?// 判斷等待隊列中是否已經(jīng)滿了,會使用到 ThreadPoolExecutor
?? ? ? ? ? ?ensurePrestart(); ?
? ? } ?
}
void ensurePrestart() { ?
? ? int wc = workerCountOf(ctl.get()); ?
? ? if (wc < corePoolSize) ?
? ? ? ? addWorker(null, true); ?
? ? else if (wc == 0) ?
? ? ? ? addWorker(null, false); ?
}

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java8中方便又實用的Map函數(shù)總結(jié)

    Java8中方便又實用的Map函數(shù)總結(jié)

    java8之后,常用的Map接口中添加了一些非常實用的函數(shù),可以大大簡化一些特定場景的代碼編寫,提升代碼可讀性,快跟隨小編一起來看看吧
    2022-11-11
  • SpringBoot下載Excel文件時,報錯文件損壞的解決方案

    SpringBoot下載Excel文件時,報錯文件損壞的解決方案

    這篇文章主要介紹了SpringBoot下載Excel文件時,報錯文件損壞的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • 詳解Spring Boot應(yīng)用的啟動和停止(start啟動)

    詳解Spring Boot應(yīng)用的啟動和停止(start啟動)

    這篇文章主要介紹了詳解Spring Boot應(yīng)用的啟動和停止(start啟動),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-12-12
  • 如何在Spring?Boot微服務(wù)使用ValueOperations操作Redis集群String字符串

    如何在Spring?Boot微服務(wù)使用ValueOperations操作Redis集群String字符串

    這篇文章主要介紹了在Spring?Boot微服務(wù)使用ValueOperations操作Redis集群String字符串類型數(shù)據(jù),本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-06-06
  • 詳談jvm--Java中init和clinit的區(qū)別

    詳談jvm--Java中init和clinit的區(qū)別

    下面小編就為大家?guī)硪黄斦刯vm--Java中init和clinit的區(qū)別。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • Java輕松使用工具類實現(xiàn)獲取MP3音頻時長

    Java輕松使用工具類實現(xiàn)獲取MP3音頻時長

    在Java中,工具類定義了一組公共方法,這篇文章將介紹Java中使用工具類來獲取一個MP3音頻文件的時間長度,感興趣的同學繼續(xù)往下閱讀吧
    2021-10-10
  • 實現(xiàn)一個簡單Dubbo完整過程詳解

    實現(xiàn)一個簡單Dubbo完整過程詳解

    這篇文章主要為大家介紹了實現(xiàn)一個簡單Dubbo完整過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-07-07
  • MyBatis?Plus實現(xiàn)中文排序的兩種有效方法

    MyBatis?Plus實現(xiàn)中文排序的兩種有效方法

    在MyBatis?Plus項目開發(fā)中,針對中文數(shù)據(jù)的排序需求是一個常見的挑戰(zhàn),尤其是在需要按照拼音或特定語言邏輯排序時,本文整合了兩種有效的方法,旨在幫助開發(fā)者克服MyBatis?Plus在處理中文排序時遇到的障礙,需要的朋友可以參考下
    2024-08-08
  • Java字符串操作全解析之語法、示例與應(yīng)用場景分析

    Java字符串操作全解析之語法、示例與應(yīng)用場景分析

    在Java算法題和日常開發(fā)中,字符串處理是必備的核心技能,本文全面梳理Java中字符串的常用操作語法,結(jié)合代碼示例、應(yīng)用場景和避坑指南,可快速掌握字符串處理技巧,輕松應(yīng)對筆試面試高頻題目,感興趣的朋友一起看看吧
    2025-04-04
  • java實現(xiàn)文件讀寫與壓縮實例

    java實現(xiàn)文件讀寫與壓縮實例

    這篇文章主要介紹了java實現(xiàn)文件讀寫與壓縮實例,有助于讀者加深對文件操作的理解,需要的朋友可以參考下
    2014-07-07

最新評論