亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java多線程案例之定時器詳解

 更新時間:2022年10月19日 08:34:30   作者:Moon?Bay  
定時器也是軟件開發(fā)中的一個重要組件.?類似于一個?“鬧鐘”.?達到一個設定的時間之后,?就執(zhí)行某個指定好的代碼。本文就來和大家詳細聊聊定時器的原理與使用,感興趣的可以了解一下

一.什么是定時器

定時器也是軟件開發(fā)中的一個重要組件. 類似于一個 “鬧鐘”. 達到一個設定的時間之后, 就執(zhí)行某個指定好的代碼

定時器是一種實際開發(fā)中非常常用的組件,我們舉幾個例子:

1.比如網(wǎng)絡通信中, 如果對方 500ms 內(nèi)沒有返回數(shù)據(jù), 則斷開連接嘗試重連

2.比如一個 Map, 希望里面的某個 key 在 3s 之后過期(自動刪除)

以上類似于這樣的場景就需要用到定時器

二.標準庫中的定時器(timer)

2.1什么是定時器

標準庫中供了一個 Timer 類. Timer 類的核心方法為 schedule ,schedule 包含兩個參數(shù). 第一個參數(shù)指定即將要執(zhí)行的任務代碼TimerTask, 第二個參數(shù)指定多長時間之后執(zhí)行 (單位為毫秒).

Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("hello");
}
}, 3000);

2.2定時器的使用

Timer的構(gòu)造方法

構(gòu)造方法說明
public Timer()無參數(shù)構(gòu)造方法,默認定時器關聯(lián)的線程不是守護線程,線程名字也是默認值
public Timer(boolean isDaemon)指定定時器中關聯(lián)的線程是否為守護線程,如果是,參數(shù)為true
public Timer(String name)指定定時器關聯(lián)線程名稱,線程類型默認為非守護線程
public Timer(String name, boolean isDaemon)指定定時器關聯(lián)線程名和線程類型

Timer方法

方法說明
public void schedule (TimerTask task, long delay)指定任務,延遲多久執(zhí)行該任務
public void schedule(TimerTask task, Date time)指定任務,指定任務的執(zhí)行時間
public void schedule(TimerTask task, long delay, long period)連續(xù)執(zhí)行指定任務,延遲時間,連續(xù)執(zhí)行任務的時間間隔,毫秒為單位
public void schedule(TimerTask task, Date firstTime, long period)連續(xù)執(zhí)行指定任務,第一次任務的執(zhí)行時間,連續(xù)執(zhí)行任務的時間間隔
public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)連續(xù)執(zhí)行指定任務,第一次任務的執(zhí)行時間,連續(xù)執(zhí)行任務的時間間隔
public void scheduleAtFixedRate(TimerTask task, long delay, long period)連續(xù)執(zhí)行指定任務,延遲時間,連續(xù)執(zhí)行任務的時間間隔,毫秒為單位
public void cancel()終止定時器所有任務,終止執(zhí)行的任務不受影響

TimerTask是專門來實現(xiàn)Runnable接口的

下面我們會實現(xiàn)一下定時器,我們就不用TimerTask了,我們直接使用Runnable,因為TimerTask實現(xiàn)了Runnable接口,所以后面測試我們自己所寫的schedule方法時,也可以傳入TimerTask類型的引用,既然是簡單地實現(xiàn),那就不實現(xiàn)連續(xù)執(zhí)行的功能了。.

public class Test {
    public static void main(String[] args){
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("執(zhí)行線程在5s后執(zhí)行");
            }
        },5000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("執(zhí)行線程在2s后執(zhí)行");
            }
        },2000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("執(zhí)行線程在3s后執(zhí)行");
            }
        },3000);
    }
}

三.實現(xiàn)定時器

3.1什么是定時器

定時器的構(gòu)成:一個帶優(yōu)先級的阻塞隊列

為啥要帶優(yōu)先級呢?

因為阻塞隊列中的任務都有各自的執(zhí)行時刻 (delay). 最先執(zhí)行的任務一定是 delay 最小的. 使用帶優(yōu)先級的隊列就可以高效的把這個 delay 最小的任務找出來.

1.隊列中的每個元素是一個 Task 對象,Task 中帶有一個時間屬性, 隊首元素就是即將同時有一個 worker 線程一直掃描隊首元素, 看隊首元素是否需要執(zhí)行

class MyTask implements Comparable<MyTask>{
    //執(zhí)行的時間戳
    private long time;
    //接受具體任務
    private Runnable runnable;
    //創(chuàng)建MyTask構(gòu)造方法
    public MyTask(Runnable runnable,long time) {
        //通過currentTimeMillis來獲取time 中存的是絕對時間, 超過這個時間的任務就應該被執(zhí)行
        this.time = System.currentTimeMillis()+time;
        this.runnable = runnable;
    }
    //執(zhí)行任務
    public void run(){
        this.runnable.run();
    }
    //提供對外time
    public long getTime() {
        return time;
    }
    //執(zhí)行comparable接口來進行時間的比較,并將time的long類型轉(zhuǎn)換為int類型
    @Override
    public int compareTo(MyTask o) {
        return (int)(this.time-o.time);
    }
}

Timer 實例中, 通過 PriorityBlockingQueue 來組織若干個 Task 對象.通過 schedule 來往隊列中插入一個個 Task 對象.

class MyTimer{
    // 定時器內(nèi)部要能夠存放多個任務
    private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
    //為鎖創(chuàng)建一個對象
    Object locker = new Object();
    public void schedule(Runnable runnable, long delay) {
        MyTask task = new MyTask(runnable, delay);
        queue.put(task);
        // 每次任務插入成功之后, 都喚醒一下掃描線程, 讓線程重新檢查一下隊首的任務看是否時間到要執(zhí)行~~
        synchronized (locker) {
            locker.notify();
        }
    }

Timer 類中存在一個 worker 線程, 一直不停的掃描隊首元素, 看看是否能執(zhí)行這個任務.所謂 “能執(zhí)行” 指的是該任務設定的時間已經(jīng)到達了

class MyTimer{
    // 定時器內(nèi)部要能夠存放多個任務
    private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
    //為鎖創(chuàng)建一個對象
    Object locker = new Object();
    public void schedule(Runnable runnable, long delay) {
        MyTask task = new MyTask(runnable, delay);
        queue.put(task);
        // 每次任務插入成功之后, 都喚醒一下掃描線程, 讓線程重新檢查一下隊首的任務看是否時間到要執(zhí)行~~
        synchronized (locker) {
            locker.notify();
        }
    }
    public MyTimer() {
        Thread t = new Thread(() -> {
            while (true) {
                try {
                    // 先取出隊首元素
                    MyTask task = queue.take();
                    // 再比較一下看看當前這個任務時間到了沒?
                    long curTime = System.currentTimeMillis();
                    if (curTime < task.getTime()) {
                        // 時間沒到, 把任務再塞回到隊列中.
                        queue.put(task);
                        // 指定一個等待時間,防止有的線程需要等待時間很長,但是線程一直運行等待時間到來執(zhí)行,這樣會占有CPU占有資源
                        synchronized (locker) {
                            locker.wait(task.getTime() - curTime);
                        }
                    } else {
                        // 時間到了, 執(zhí)行這個任務
                        task.run();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }
}

3.2最終實現(xiàn)代碼

package thread;

import java.util.PriorityQueue;
import java.util.concurrent.PriorityBlockingQueue;

// 創(chuàng)建一個類, 表示一個任務.
class MyTask implements Comparable<MyTask> {
    // 任務具體要干啥
    private Runnable runnable;
    // 任務具體啥時候干. 保存任務要執(zhí)行的毫秒級時間戳
    private long time;

    // after 是一個時間間隔. 不是絕對的時間戳的值
    public MyTask(Runnable runnable, long delay) {
        this.runnable = runnable;
        this.time = System.currentTimeMillis() + delay;
    }

    public void run() {
        runnable.run();
    }

    public long getTime() {
        return time;
    }

    @Override
    public int compareTo(MyTask o) {
        // 到底是誰見誰, 才是一個時間小的在前? 需要咱們背下來.
        return (int) (this.time - o.time);
    }
}

class MyTimer {
    // 定時器內(nèi)部要能夠存放多個任務
    private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();

    public void schedule(Runnable runnable, long delay) {
        MyTask task = new MyTask(runnable, delay);
        queue.put(task);
        // 每次任務插入成功之后, 都喚醒一下掃描線程, 讓線程重新檢查一下隊首的任務看是否時間到要執(zhí)行~~
        synchronized (locker) {
            locker.notify();
        }
    }

    private Object locker = new Object();

    public MyTimer() {
        Thread t = new Thread(() -> {
            while (true) {
                try {
                    // 先取出隊首元素
                    MyTask task = queue.take();
                    // 再比較一下看看當前這個任務時間到了沒?
                    long curTime = System.currentTimeMillis();
                    if (curTime < task.getTime()) {
                        // 時間沒到, 把任務再塞回到隊列中.
                        queue.put(task);
                        // 指定一個等待時間
                        synchronized (locker) {
                            locker.wait(task.getTime() - curTime);
                        }
                    } else {
                        // 時間到了, 執(zhí)行這個任務
                        task.run();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }
}

public class Test {
    public static void main(String[] args) {
        MyTimer myTimer = new MyTimer();
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello timer!");
            }
        }, 3000);
        System.out.println("main");
    }
}

以上就是Java多線程案例之定時器詳解的詳細內(nèi)容,更多關于Java多線程 定時器的資料請關注腳本之家其它相關文章!

相關文章

  • 詳解spring boot容器加載完后執(zhí)行特定操作

    詳解spring boot容器加載完后執(zhí)行特定操作

    這篇文章主要介紹了詳解spring boot容器加載完后執(zhí)行特定操作,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-01-01
  • 關于slf4j_log4j2源碼學習心得

    關于slf4j_log4j2源碼學習心得

    這篇文章主要介紹了slf4j_log4j2源碼學習心得,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • 一文詳解RabbitMQ如何保證消息可靠性

    一文詳解RabbitMQ如何保證消息可靠性

    這篇文章將詳細介紹RabbitMQ的消息可靠性機制,如消息丟失,消息重復性消費,消息積壓等問題,具有一定的參考價值,需要的朋友可以參考下
    2023-07-07
  • Eclipse中maven的配置詳解

    Eclipse中maven的配置詳解

    這篇文章主要介紹了Eclipse中maven的配置詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-10-10
  • Mybatis 中如何判斷集合的size

    Mybatis 中如何判斷集合的size

    這篇文章主要介紹了在Mybatis中判斷集合的size操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • Java設計模式探究之觀察者模式詳解

    Java設計模式探究之觀察者模式詳解

    這篇文章主要為大家詳細介紹了JAVA的觀察者模式,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-08-08
  • java遞歸菜單樹轉(zhuǎn)換成pojo對象

    java遞歸菜單樹轉(zhuǎn)換成pojo對象

    這篇文章介紹了java遞歸菜單樹轉(zhuǎn)換成pojo對象的具體實現(xiàn),有需要的朋友可以參考一下
    2013-08-08
  • Lambda表達式的使用及注意事項

    Lambda表達式的使用及注意事項

    這篇文章主要介紹了Lambda表達式的使用及注意事項,主要圍繞?Lambda表達式的省略模式?Lambda表達式和匿名內(nèi)部類的區(qū)別的相關內(nèi)容展開詳情,感興趣的小伙伴可以參考一下
    2022-06-06
  • Java字符串中刪除指定子字符串的方法簡介

    Java字符串中刪除指定子字符串的方法簡介

    這篇文章主要介紹了Java字符串中刪除指定子字符串的方法,是Java入門學習中的基礎知識,需要的朋友可以參考下
    2015-11-11
  • Springboot+echarts實現(xiàn)可視化

    Springboot+echarts實現(xiàn)可視化

    這篇文章主要為大家詳細介紹了Springboot+echarts實現(xiàn)可視化,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-12-12

最新評論