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

一文帶你搞懂Java定時器Timer的使用

 更新時間:2023年01月09日 15:30:57   作者:熬夜磕代碼丶  
定時器類似于我們生活中的鬧鐘,可以設(shè)定一個時間來提醒我們。而定時器是指定一個時間去執(zhí)行一個任務(wù),讓程序去代替人工準時操作。本文就來聊聊Java定時器Timer的使用,需要的可以參考一下

一、定時器是什么

定時器類似于我們生活中的鬧鐘,可以設(shè)定一個時間來提醒我們。

而定時器是指定一個時間去執(zhí)行一個任務(wù),讓程序去代替人工準時操作。

標準庫中的定時器: Timer

方法作用
void schedule(TimerTask task, long delay)指定delay時間之后(單位毫秒)執(zhí)行任務(wù)task
public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定時器任務(wù)! ");
            }
        },1000);
    }

這段程序就是創(chuàng)建一個定時器,然后提交一個1000s后執(zhí)行的任務(wù)。

二、自定義定時器

我們自己實現(xiàn)一個定時器的前提是我們需要弄清楚定時器都有什么:

1.一個掃描線程,負責來判斷任務(wù)是否到時間需要執(zhí)行

2.需要有一個數(shù)據(jù)結(jié)構(gòu)來保存我們定時器中提交的任務(wù)

創(chuàng)建一個掃描線程相對比較簡單,我們需要確定一個數(shù)據(jù)結(jié)構(gòu)來保存我們提交的任務(wù),我們提交過來的任務(wù),是由任務(wù)和時間組成的,我們需要構(gòu)建一個Task對象,數(shù)據(jù)結(jié)構(gòu)我們這里使用優(yōu)先級隊列,因為我們的任務(wù)是有時間順序的,具有一個優(yōu)先級,并且要保證在多線程下是安全的,所以我們這里使用:PriorityBlockingQueue比較合適。

首先我們構(gòu)造一個Task對象

class MyTask {
    //即將執(zhí)行的任務(wù)
    private Runnable runnable;
    //在多久后執(zhí)行
    private long time;

    public MyTask(Runnable runnable, long time) {
        this.runnable = runnable;
        this.time = time;
    }

    public long getTime() {
        return time;
    }
    
    //執(zhí)行任務(wù)
    public void run() {
        runnable.run();
    }
}

MyTimer類:

public class MyTimer {
    //掃描線程
    private Thread t;
    //創(chuàng)建一個阻塞優(yōu)先級隊列,用來保存提交的Task對象
    private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
    private Object locker = new Object();

    //提交任務(wù)的方法
    public void schedule(Runnable runnable,long time) {
        //這里我們的時間換算一下,保存實際執(zhí)行的時間
        MyTask task = new MyTask(runnable,System.currentTimeMillis() +  time);
        queue.put(task);
    }

    //構(gòu)建掃描線程
    public MyTimer() {
        t = new Thread(() -> {
           //我們?nèi)〕鲫犃兄袝r間最近的元素
            while (true) {
                try {
                    MyTask task = queue.take();
                    long curTime = System.currentTimeMillis();
                    if(curTime < task.getTime()) {
                        //證明還沒到執(zhí)行的時間,再放進隊列
                        queue.put(task);
                    } else {
                        //到時間了,執(zhí)行任務(wù)
                        task.run();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }
    t.start();
}

雖然我們大體已經(jīng)寫出來了,但是我們這個定時器實現(xiàn)的還有一些問題。

問題1:既然我們是優(yōu)先級隊列,我們再阻塞優(yōu)先級隊列中放入Task對象時,是根據(jù)什么建立堆的?

我們發(fā)現(xiàn)當我們運行程序時,我們的程序也會報這樣的錯誤。

class MyTask implements Comparable<MyTask>{
    //即將執(zhí)行的任務(wù)
    private Runnable runnable;
    //在多久后執(zhí)行
    private long time;

    public MyTask(Runnable runnable, long time) {
        this.runnable = runnable;
        this.time = time;
    }

    public long getTime() {
        return time;
    }

    //執(zhí)行任務(wù)
    public void run() {
        runnable.run();
    }

    @Override
    public int compareTo(MyTask o) {
        return (int) (this.time - o.time);
    }
}

我們需要實現(xiàn)Comparable接口并且重寫compareTo方法,指明我們是根據(jù)時間來決定在隊列中的優(yōu)先級。

2.我們的掃描線程,掃描的速度太快,造成了不必要的CPU資源浪費。

比如我們早上8.00提交了一個中午12.00的任務(wù),那么我們這樣的程序就會從8.00一直循環(huán)幾十億次,而這樣的等待是沒有任何意義的。

更合理的方式是,不要在這里忙等,而是“阻塞式”等待。

public MyTimer() {
        t = new Thread(() -> {
           //我們?nèi)〕鲫犃兄袝r間最近的元素
            while (true) {
                try {
                    MyTask task = queue.take();
                    long curTime = System.currentTimeMillis();
                    if(curTime < task.getTime()) {
                        //證明還沒到執(zhí)行的時間,再放進隊列
                        queue.put(task);
                        synchronized (locker) {
                            locker.wait(task.getTime() - curTime);
                        }
                    } else {
                        //到時間了,執(zhí)行任務(wù)
                        task.run();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }
    t.start();

我們重寫一下掃描線程,進行修改,當我們判斷隊列中最近的一個任務(wù)的時間都沒到時,我們的掃描線程就進行阻塞等待,這里我們使用的不是wait(),而是wait(long time),我們傳入的參數(shù)是要執(zhí)行的時間和當前時間的差值,有的同學可能會問了,那這樣執(zhí)行的時候和預(yù)期執(zhí)行的時間不就有出入了嘛?

因為我們程序里的定時操作,本來就難以做到非常準確,因為操作系統(tǒng)調(diào)度是隨機的,有一定的時間開銷,存在ms的誤差都是相當正常的,不影響我們的正常使用。

我們上面進行阻塞等待,難道就傻傻的等到時間到了自動喚醒嘛? 有沒有啥特殊情況呢?這里是有的,比如我們設(shè)定了一個阻塞到12點在喚醒,但我們又提交了一個10點的新任務(wù),那么我們就應(yīng)該提前喚醒了,所以我們應(yīng)該在每次提交任務(wù)后都進行主動喚醒,再由我們掃描線程決定是執(zhí)行還是繼續(xù)阻塞等待。

public void schedule(Runnable runnable,long time) {
        //這里我們的時間換算一下,保存實際執(zhí)行的時間
        MyTask task = new MyTask(runnable,System.currentTimeMillis() +  time);
        queue.put(task);
        synchronized (locker) {
            locker.notify();
        }
    }

即使我們現(xiàn)在所有正常的情況都考慮到了,但是我們這里仍然存在一種極端的情況。

假設(shè)我們的掃描線程剛執(zhí)行完put方法,這個線程就被cpu調(diào)度走了,此時我們的另一個線程調(diào)用了schedule,添加了新任務(wù),新任務(wù)是10點執(zhí)行,然后notify,因為我們并沒有wait(),所以相當于這里是空的notify,然后我們的線程調(diào)度回來去執(zhí)行wait()方法,但是我們的時間差仍然是之前算好的時間差,從8.00點到12.00點,這樣就會產(chǎn)生很大的錯誤。

這里造成這樣的問題,是因為我們的take操作和wait操作不是原子的,我們需要在take和wait之間加上鎖,保證每次notify的時候,都在wait中。

public MyTimer() {
        t = new Thread(() -> {
            while (true) {
                try {
                    synchronized (locker) {
                        MyTask Task = queue.take();
                        long curTime = System.currentTimeMillis();
                        if (curTime < Task.getTime()) {
                            queue.put(Task);
                            locker.wait(Task.getTime() - curTime);
                        } else {
                            Task.run();
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();

以上就是一文帶你搞懂Java定時器Timer的使用的詳細內(nèi)容,更多關(guān)于Java定時器Timer的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論