最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java?Timer單線程下的定時(shí)任務(wù)舉例詳解

 更新時(shí)間:2025年10月27日 10:41:23   作者:嗎嘍什么時(shí)候禿頭  
在日常的項(xiàng)目開發(fā)中,多多少少都會涉及到一些定時(shí)任務(wù)的需求,下面這篇文章主要介紹了Java?Timer單線程下定時(shí)任務(wù)的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下

引言

Timer 是 Java 標(biāo)準(zhǔn)庫(java.util 包)中用于 定時(shí)調(diào)度任務(wù) 的工具類,它允許程序在指定時(shí)間點(diǎn)執(zhí)行任務(wù),或按固定周期重復(fù)執(zhí)行任務(wù)。簡單來說,Timer 就像一個(gè) “定時(shí)器”,可以幫你規(guī)劃代碼在未來的某個(gè)時(shí)間運(yùn)行,或周期性地運(yùn)行。

在學(xué)習(xí) Timer 的用法和底層原理前,最需要了解的核心數(shù)據(jù)結(jié)構(gòu)是 優(yōu)先級隊(duì)列,具體來說是 Timer 內(nèi)部使用的 基于小頂堆實(shí)現(xiàn)的優(yōu)先級隊(duì)列。

什么是小頂堆

小頂堆是一顆完全二叉樹,它的根節(jié)點(diǎn)是最小的元素,然后每個(gè)子節(jié)點(diǎn)都必須比它的父節(jié)點(diǎn)存的值要大。

在 Timer 中,小頂堆(TaskQueue)的堆頂元素是 “最早需要執(zhí)行的定時(shí)任務(wù)(TimerTask)”。

具體來說,TaskQueue 會按照任務(wù)的 nextExecutionTime(下一次執(zhí)行時(shí)間)對所有 TimerTask 進(jìn)行排序,而小頂堆的堆頂元素就是 nextExecutionTime 最小的任務(wù),也就是當(dāng)前所有任務(wù)中 “最早到點(diǎn)需要執(zhí)行” 的那個(gè)任務(wù)。

Timer 類的核心結(jié)構(gòu)

public class Timer {
    // 任務(wù)隊(duì)列(基于小頂堆實(shí)現(xiàn))
    private final TaskQueue queue = new TaskQueue();
    // 調(diào)度線程(負(fù)責(zé)執(zhí)行任務(wù))
    private final TimerThread thread = new TimerThread(queue);
    
    // 構(gòu)造方法:啟動調(diào)度線程
    public Timer() {
        this("Timer-" + serialNumber());
    }
    
    public Timer(String name) {
        thread.setName(name);
        thread.start(); // 啟動TimerThread
    }
    
    // 提交任務(wù)的核心方法(以schedule為例)
    public void schedule(TimerTask task, Date time) {
        // 計(jì)算延遲時(shí)間(相對于當(dāng)前時(shí)間)
        long delay = time.getTime() - System.currentTimeMillis();
        if (delay < 0) delay = 0;
        // 調(diào)用內(nèi)部方法添加任務(wù)
        sched(task, System.currentTimeMillis() + delay, 0);
    }
    
    // 實(shí)際添加任務(wù)到隊(duì)列的方法
    private void sched(TimerTask task, long time, long period) {
        if (time < 0) throw new IllegalArgumentException("Illegal execution time.");
        
        // 同步鎖:保證任務(wù)隊(duì)列操作的線程安全
        synchronized(queue) {
            if (thread.isInterrupted())
                throw new IllegalStateException("Timer already cancelled.");
            
            // 設(shè)置任務(wù)的執(zhí)行時(shí)間和周期
            task.nextExecutionTime = time;
            task.period = period;
            task.state = TimerTask.SCHEDULED;
            
            // 將任務(wù)添加到隊(duì)列(小頂堆)
            queue.add(task);
            // 如果添加的是堆頂任務(wù),喚醒調(diào)度線程(可能在等待中)
            if (queue.getMin() == task)
                queue.notify();
        }
    }
}

任務(wù)隊(duì)列(TaskQueue):基于小頂堆的實(shí)現(xiàn)

在Java中,不管是二叉樹,還是堆,它們都只是邏輯結(jié)構(gòu),并不是真正意義上的存儲結(jié)構(gòu),具體的存儲結(jié)構(gòu)都是基于數(shù)組或者鏈表來實(shí)現(xiàn)的。

class TaskQueue {
    // 用數(shù)組存儲堆元素(小頂堆)
    private TimerTask[] queue = new TimerTask[128];
    private int size = 0; // 當(dāng)前任務(wù)數(shù)量
    
    // 添加任務(wù)(入隊(duì))
    void add(TimerTask task) {
        // 擴(kuò)容邏輯(當(dāng)數(shù)組滿時(shí))
        if (size + 1 == queue.length)
            queue = Arrays.copyOf(queue, 2 * queue.length);
        
        queue[size++] = task;
        // 上浮調(diào)整:維持小頂堆特性
        fixUp(size - 1);
    }
    
    // 獲取堆頂任務(wù)(最早執(zhí)行的任務(wù))
    TimerTask getMin() {
        return queue[0];
    }
    
    // 移除堆頂任務(wù)(出隊(duì))
    void removeMin() {
        queue[0] = queue[--size];
        // 下沉調(diào)整:維持小頂堆特性
        fixDown(0);
    }
    
    // 上浮操作:新元素插入后,向上調(diào)整堆
    private void fixUp(int k) {
        while (k > 0) {
            int j = (k - 1) >>> 1; // 父節(jié)點(diǎn)下標(biāo)
            if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)
                break; // 父節(jié)點(diǎn)更小,無需調(diào)整
            // 交換父節(jié)點(diǎn)和當(dāng)前節(jié)點(diǎn)
            TimerTask tmp = queue[j];
            queue[j] = queue[k];
            queue[k] = tmp;
            k = j;
        }
    }
    
    // 下沉操作:移除堆頂后,向下調(diào)整堆
    private void fixDown(int k) {
        int j;
        while ((j = (k << 1) + 1) < size) { // 左子節(jié)點(diǎn)下標(biāo)
            // 找到左右子節(jié)點(diǎn)中較小的那個(gè)
            if (j + 1 < size && queue[j + 1].nextExecutionTime < queue[j].nextExecutionTime)
                j++;
            if (queue[k].nextExecutionTime <= queue[j].nextExecutionTime)
                break; // 當(dāng)前節(jié)點(diǎn)更小,無需調(diào)整
            // 交換當(dāng)前節(jié)點(diǎn)和子節(jié)點(diǎn)
            TimerTask tmp = queue[k];
            queue[k] = queue[j];
            queue[j] = tmp;
            k = j;
        }
    }
}

調(diào)度線程(TimerThread):任務(wù)執(zhí)行的核心邏輯

TimerThread 是 Timer 的內(nèi)部線程類,負(fù)責(zé)從隊(duì)列中取任務(wù)并執(zhí)行:

class TimerThread extends Thread {
    private final TaskQueue queue;
    
    TimerThread(TaskQueue queue) {
        this.queue = queue;
    }
    
    public void run() {
        try {
            mainLoop(); // 核心循環(huán):不斷執(zhí)行任務(wù)
        } finally {
            // 線程退出時(shí),標(biāo)記所有任務(wù)為取消狀態(tài)
            synchronized(queue) {
                queue.isDisposed = true;
                queue.queue = null;
            }
        }
    }
    
    // 核心循環(huán):調(diào)度任務(wù)的主邏輯
    private void mainLoop() {
        while (true) {
            try {
                TimerTask task;
                boolean taskFired;
                
                // 同步獲取任務(wù)
                synchronized(queue) {
                    // 等待隊(duì)列中有任務(wù)
                    while (queue.size() == 0 && !queue.isDisposed)
                        queue.wait();
                    if (queue.isDisposed)
                        break; // 隊(duì)列已銷毀,退出循環(huán)
                    
                    // 獲取堆頂任務(wù)(最早執(zhí)行的任務(wù))
                    long currentTime, executionTime;
                    task = queue.getMin();
                    synchronized(task.lock) {
                        if (task.state != TimerTask.SCHEDULED) {
                            queue.removeMin(); // 任務(wù)已取消,移除
                            continue;
                        }
                        currentTime = System.currentTimeMillis();
                        executionTime = task.nextExecutionTime;
                        // 檢查是否到達(dá)執(zhí)行時(shí)間
                        if (executionTime > currentTime) {
                            // 未到時(shí)間:等待差值時(shí)間
                            queue.wait(executionTime - currentTime);
                            taskFired = false;
                        } else {
                            // 已到時(shí)間:移除堆頂任務(wù)
                            queue.removeMin();
                            taskFired = true;
                        }
                    }
                }
                
                // 執(zhí)行任務(wù)(如果已到時(shí)間)
                if (taskFired) {
                    task.run(); // 調(diào)用TimerTask的run()方法
                    // 處理周期性任務(wù):重新計(jì)算下次執(zhí)行時(shí)間并加入隊(duì)列
                    if (task.period != 0) {
                        synchronized(task.lock) {
                            task.nextExecutionTime = 
                                task.period < 0 ? currentTime - task.period 
                                                : executionTime + task.period;
                            queue.add(task); // 重新入隊(duì)
                        }
                    }
                }
            } catch (InterruptedException e) {
                // 忽略中斷,繼續(xù)循環(huán)
            }
        }
    }
}

代碼使用演示

package com.ape.test;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Test {
    public static void main(String[] args) {
        Timer timer = new Timer();
        final int totalTasks = 3; // 總?cè)蝿?wù)數(shù)

        for(int i = 0; i < totalTasks; i++){
            TimerTask task = new FooTimerTask("task" + i, timer, totalTasks);
            timer.schedule(task, new Date());
        }
    }
}

class FooTimerTask extends TimerTask{
    private String name;
    private Timer timer;
    private int totalTasks;
    private static int completedCount = 0; // 已完成任務(wù)計(jì)數(shù)(靜態(tài)變量共享)

    public FooTimerTask(String name, Timer timer, int totalTasks) {
        this.name = name;
        this.timer = timer;
        this.totalTasks = totalTasks;
    }

    @Override
    public void run() {
        try {
            System.out.println("name:" + name + " startTime=" + new Date());
            Thread.sleep(3000);
            System.out.println("end:" + name + " endTime=" + new Date());
            System.out.println("----------------------------------------");

            // 任務(wù)完成后計(jì)數(shù),最后一個(gè)任務(wù)完成時(shí)取消Timer,如果不取消Timer,進(jìn)程不會結(jié)束
            synchronized (FooTimerTask.class) {
                completedCount++;
                if (completedCount == totalTasks) {
                    timer.cancel();
                    System.out.println("所有任務(wù)執(zhí)行完畢,Timer已終止");
                }
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

運(yùn)行結(jié)果

Timer的缺點(diǎn)

  1. 單線程串行執(zhí)行,效率低所有任務(wù)依賴唯一的 TimerThread 執(zhí)行,若某個(gè)任務(wù)耗時(shí)過長(如 IO 阻塞、長時(shí)間計(jì)算),會阻塞后續(xù)所有任務(wù),即使它們已到執(zhí)行時(shí)間。例:任務(wù) A 執(zhí)行需 10 秒,任務(wù) B 本應(yīng)在 2 秒后執(zhí)行,實(shí)際會延遲到 A 結(jié)束后才開始。

  2. 異常敏感,易導(dǎo)致整體崩潰若某個(gè) TimerTask 的 run() 方法拋出未捕獲異常,會直接導(dǎo)致 TimerThread 終止,所有未執(zhí)行的任務(wù)全部失效,且不會有任何提示。

  3. 時(shí)間依賴系統(tǒng)時(shí)鐘,穩(wěn)定性差任務(wù)的執(zhí)行時(shí)間依賴系統(tǒng)時(shí)間,若系統(tǒng)時(shí)間被手動調(diào)整(如向前回?fù)埽?,可能?dǎo)致任務(wù)執(zhí)行混亂(如周期性任務(wù)被重復(fù)執(zhí)行)。

  4. 不支持多線程并發(fā)無法利用多核 CPU 資源,所有任務(wù)只能串行執(zhí)行,不適合高并發(fā)或任務(wù)量大的場景。

  5. 線程生命周期管理繁瑣Timer 線程默認(rèn)是用戶線程,若不主動調(diào)用 timer.cancel(),即使所有任務(wù)執(zhí)行完畢,線程也會一直運(yùn)行,導(dǎo)致程序無法退出。

適用場景與替代方案

  • 適合場景:單線程、任務(wù)執(zhí)行時(shí)間短、無并發(fā)需求的簡單定時(shí)任務(wù)(如簡單的延遲提醒、臨時(shí)調(diào)度)。
  • 不適合場景:多任務(wù)并發(fā)、任務(wù)耗時(shí)較長、需要高穩(wěn)定性的場景(如分布式定時(shí)任務(wù)、核心業(yè)務(wù)調(diào)度)。
  • 推薦替代方案: ScheduledExecutorService,基于線程池實(shí)現(xiàn),解決了 Timer 的單線程瓶頸和異常敏感問題,支持多線程并行執(zhí)行,是更現(xiàn)代、更健壯的選擇。

在下一篇文章中說說ScheduleExecutorService

總結(jié)

到此這篇關(guān)于Java Timer單線程下的定時(shí)任務(wù)的文章就介紹到這了,更多相關(guān)Java Timer單線程定時(shí)任務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • mybatis執(zhí)行錯誤但sql執(zhí)行正常問題

    mybatis執(zhí)行錯誤但sql執(zhí)行正常問題

    這篇文章主要介紹了mybatis執(zhí)行錯誤但sql執(zhí)行正常問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • JDK環(huán)境變量配置的具體操作步驟

    JDK環(huán)境變量配置的具體操作步驟

    本篇文章介紹了,JDK環(huán)境變量配置的具體操作步驟。需要的朋友參考下
    2013-05-05
  • 自定義feignClient的常見坑及解決

    自定義feignClient的常見坑及解決

    這篇文章主要介紹了自定義feignClient的常見坑及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • jpanel設(shè)置背景圖片的二個(gè)小例子

    jpanel設(shè)置背景圖片的二個(gè)小例子

    這篇文章主要介紹了jpanel設(shè)置背景圖片的二個(gè)小例子,實(shí)現(xiàn)了動態(tài)加載圖片做背景的方法,需要的朋友可以參考下
    2014-03-03
  • SpringBoot?整合mapstruct的實(shí)現(xiàn)步驟

    SpringBoot?整合mapstruct的實(shí)現(xiàn)步驟

    這篇文章主要介紹了SpringBoot整合mapstruct,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • Mybatis-Plus條件構(gòu)造器select方法返回指定字段方式

    Mybatis-Plus條件構(gòu)造器select方法返回指定字段方式

    這篇文章主要介紹了Mybatis-Plus條件構(gòu)造器select方法返回指定字段方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • 詳解mybatis generator代碼生成器的使用

    詳解mybatis generator代碼生成器的使用

    MyBatis Generator(MBG)是MyBatis MyBatis 和iBATIS的代碼生成器。這篇文章主要介紹了mybatis generator代碼生成器的使用,需要的朋友可以參考下
    2021-09-09
  • SpringMVC文件上傳及查看的示例代碼

    SpringMVC文件上傳及查看的示例代碼

    本篇文章主要介紹了SpringMVC文件上傳及查看的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-01-01
  • Java?Servlet響應(yīng)httpServletResponse過程詳解

    Java?Servlet響應(yīng)httpServletResponse過程詳解

    HttpServletResponse是處理http響應(yīng)的對象,調(diào)用該對象的方法,設(shè)置到對象屬性的內(nèi)容,tomcat最終會組織為http響應(yīng)報(bào)文
    2022-02-02
  • SpringBoot集成EasyExcel的步驟

    SpringBoot集成EasyExcel的步驟

    EasyExcel是阿里巴巴開源poi插件之一,主要解決了poi框架使用復(fù)雜,sax解析模式不容易操作,數(shù)據(jù)量大起來容易OOM,解決了POI并發(fā)造成的報(bào)錯。主要解決方式:通過解壓文件的方式加載,一行一行的加載,并且拋棄樣式字體等不重要的數(shù)據(jù),降低內(nèi)存的占用。
    2021-06-06

最新評論

朝阳县| 永济市| 英山县| 五台县| 比如县| 本溪市| 白银市| 顺昌县| 万安县| 来宾市| 南部县| 广元市| 孟津县| 卓资县| 旅游| 布尔津县| 合肥市| 鄱阳县| 东宁县| 涞水县| 瑞丽市| 铁力市| 大姚县| 枣庄市| 灵寿县| 荣成市| 凤庆县| 托克托县| 武胜县| 油尖旺区| 永安市| 巫溪县| 平罗县| 灵寿县| 金山区| 淮阳县| 隆德县| 沙田区| 吉木乃县| 河津市| 长汀县|