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

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

 更新時間:2023年01月28日 16:38:34   作者:bit榮  
定時器是一種實際開發(fā)中非常常用的組件,?類似于一個?“鬧鐘”,?達到一個設定的時間之后,?就執(zhí)行某個指定好的代碼。本文主要來和大家聊聊定時器的原理與使用,需要的可以參考一下

一. 定時器概述

1. 什么是定時器

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

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

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

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

2. 標準庫中的定時器

標準庫中提供了一個 Timer 類, Timer 類的核心方法為schedule.

Timer類構造時內部會創(chuàng)建線程, 有下面的四個構造方法, 可以指定線程名和是否將定時器內部的線程指定為后臺線程(即守護線程), 如果不指定, 定時器對象內部的線程默認為前臺線程.

序號構造方法解釋
1public Timer()無參, 定時器關聯(lián)的線程為前臺線程, 線程名為默認值
2public Timer(boolean isDaemon)指定定時器中關聯(lián)的線程類型, true(后臺線程), false(前臺線程)
3public Timer(String name)指定定時器關聯(lián)的線程名, 線程類型為前臺線程
4public Timer(String name, boolean isDaemon) 指定定時器關聯(lián)的線程名和線程類型

schedule 方法是給Timer注冊一個任務, 這個任務在指定時間后進行執(zhí)行, TimerTask類就是專門描述定時器任務的一個抽象類, 它實現(xiàn)了Runnable接口.

public abstract class TimerTask implements Runnable // jdk源碼
序號方法解釋
1public void schedule(TimerTask task, long delay)指定任務, 延遲多久執(zhí)行該任務
2public void schedule(TimerTask task, Date time)指定任務, 指定任務的執(zhí)行時間
3public void schedule(TimerTask task, long delay, long period)連續(xù)執(zhí)行指定任務, 延遲時間, 連續(xù)執(zhí)行任務的時間間隔, 毫秒為單位
4public void schedule(TimerTask task, Date firstTime, long period)連續(xù)執(zhí)行指定任務, 第一次任務的執(zhí)行時間, 連續(xù)執(zhí)行任務的時間間隔
5public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)與方法4作用相同
6public void scheduleAtFixedRate(TimerTask task, long delay, long period)與方法3作用相同
7public void cancel()清空任務隊列中的全部任務, 正在執(zhí)行的任務不受影響

代碼示例:

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

public class TestProgram {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("執(zhí)行延后3s的任務!");
            }
        }, 3000);

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("執(zhí)行延后2s后的任務!");
            }
        }, 2000);
        
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("執(zhí)行延后1s的任務!");
            }
        }, 1000);
    }
}

執(zhí)行結果:

觀察執(zhí)行結果, 任務執(zhí)行結束后程序并沒有結束, 即進程并沒有結束, 這是因為上面的代碼定時器內部是開啟了一個線程去執(zhí)行任務的, 雖然任務執(zhí)行完成了, 但是該線程并沒有銷毀; 這和自己定義一個線程執(zhí)行完成 run 方法后就自動銷毀是不一樣的, Timer 本質上是相當于線程池, 它緩存了一個工作線程, 一旦任務執(zhí)行完成, 該工作線程就處于空閑狀態(tài), 等待下一輪任務.

二. 定時器的簡單實現(xiàn)

首先, 我們需要定義一個類, 用來描述一個定時器當中的任務, 類要成員要有一個Runnable, 再加上一個任務執(zhí)行的時間戳, 具體還包含如下內容:

  • 構造方法, 用來指定任務和任務的延遲執(zhí)行時間.
  • 兩個get方法, 分別用來給外部對象獲取該對象的任務和執(zhí)行時間.
  • 實現(xiàn)Comparable接口, 指定比較方式, 用于判斷定時器任務的執(zhí)行順序, 每次需要執(zhí)行時間最早的任務.
class MyTask implements Comparable<MyTask>{
    //要執(zhí)行的任務
    private Runnable runnable;
    //任務的執(zhí)行時間
    private long time;

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

    //獲取當前任務的執(zhí)行時間
    public long getTime() {
        return this.time;
    }
    //執(zhí)行任務
    public void run() {
        runnable.run();
    }

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

然后就需要實現(xiàn)定時器類了, 我們需要使用一個數(shù)據(jù)結構來組織定時器中的任務, 需要每次都能將時間最早的任務找到并執(zhí)行, 這個情況我們可以考慮用優(yōu)先級隊列(即小根堆)來實現(xiàn), 當然我們還需要考慮線程安全的問題, 所以我們選用優(yōu)先級阻塞隊列 PriorityBlockingQueue 是最合適的, 特別要注意在自定義的任務類當中要實現(xiàn)比較方式, 或者實現(xiàn)一下比較器也行.

private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();

我們自己實現(xiàn)的定時器類中要有一個注冊任務的方法, 用來將任務插入到優(yōu)先級阻塞隊列中;

還需要有一個線程用來執(zhí)行任務, 這個線程是從優(yōu)先級阻塞隊列中取出隊首任務去執(zhí)行, 如果這個任務還沒有到執(zhí)行時間, 那么線程就需要把這個任務再放會隊列當中, 然后線程就進入等待狀態(tài), 線程等待可以使用sleep和wait, 但這里有一個情況需要考慮, 當有新任務插入到隊列中時, 我們需要喚醒線程重新去優(yōu)先級阻塞隊列拿隊首任務, 畢竟新注冊的任務的執(zhí)行時間可能是要比前一陣拿到的隊首任務時間是要早的, 所以這里使用wait進行進行阻塞更合適, 那么喚醒操作就需要使用notify來實現(xiàn)了.

實現(xiàn)代碼如下:

//自己實現(xiàn)的定時器類
class MyTimer {
    //掃描線程
    private Thread t = null;
    //阻塞隊列,存放任務
    private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();

    public MyTimer() {
        //構造掃描線程
        t = new Thread(() -> {
           while (true) {
               //取出隊首元素,檢查隊首元素執(zhí)行任務的時間
               //時間沒到,再把任務放回去
               //時間到了,就執(zhí)行任務
               try {
                   synchronized (this) {
                       MyTask task = queue.take();
                       long curTime = System.currentTimeMillis();
                       if (curTime < task.getTime()) {
                           //時間沒到,放回去
                           queue.put(task);
                           //放回任務后,不應該立即就再次取出該任務
                           //所以wait設置一個阻塞等待,以便新任務到時間或者新任務來時后再取出來
                           this.wait(task.getTime() - curTime);
                       } else {
                           //時間到了,執(zhí)行任務
                           task.run();
                       }
                   }
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);

               }
           }
        });
        t.start();
    }

    /**
     * 注冊任務的方法
     * @param runnable 任務內容
     * @param after 表示在多少毫秒之后執(zhí)行. 形如 1000
     */
    public void schedule (Runnable runnable, long after) {
        //獲取當前時間的時間戳再加上任務時間
        MyTask task = new MyTask(runnable, System.currentTimeMillis() + after);
        queue.put(task);
        //每次當新任務加載到阻塞隊列時,需要中途喚醒線程,因為新進來的任務可能是最早需要執(zhí)行的
        synchronized (this) {
            this.notify();
        }
    }
}

要注意上面掃描線程中的synchronized并不能只要針對wait方法加鎖, 如果只針對wait加鎖的話, 考慮一個極端的情況, 假設的掃描線程剛執(zhí)行完put方法, 這個線程就被cpu調度走了, 此時另有一個線程在隊列中插入了新任務, 然后notify喚醒了線程, 而剛剛并沒有執(zhí)行wait阻塞, notify就沒有起到什么作用, 當cpu再調度到這個線程, 這樣的話如果新插入的任務要比原來隊首的任務時間更早, 那么這個新任務就被錯過了執(zhí)行時間, 這些線程安全問題真是防不勝防啊, 所以我們需要保證這些操作的原子性, 也就是上面的代碼, 擴大鎖的范圍, 保證每次notify都是有效的.

那么最后基于上面的代碼, 我們來測試一下這個定時器:

public class TestDemo23 {
    public static void main(String[] args) {
        MyTimer timer = new MyTimer();
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("2s后執(zhí)行的任務1");
            }
        }, 2000);

        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("2s后執(zhí)行的任務1");
            }
        }, 1000);
    }
}

執(zhí)行結果:

到此這篇關于Java多線程案例之定時器詳解的文章就介紹到這了,更多相關Java定時器內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • java讀寫oracle的blob字段示例

    java讀寫oracle的blob字段示例

    這篇文章主要介紹了java讀寫oracle的blob字段示例,需要的朋友可以參考下
    2014-02-02
  • Springboot @Configuration @bean注解作用解析

    Springboot @Configuration @bean注解作用解析

    這篇文章主要介紹了springboot @Configuration @bean注解作用解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-02-02
  • redisson.tryLock()參數(shù)的使用及理解

    redisson.tryLock()參數(shù)的使用及理解

    這篇文章主要介紹了redisson.tryLock()參數(shù)的使用,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • Spring mvc如何實現(xiàn)數(shù)據(jù)處理

    Spring mvc如何實現(xiàn)數(shù)據(jù)處理

    這篇文章主要介紹了Spring mvc如何實現(xiàn)數(shù)據(jù)處理,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-03-03
  • java實現(xiàn)合并圖片的方法示例

    java實現(xiàn)合并圖片的方法示例

    這篇文章主要介紹了java實現(xiàn)合并圖片的方法,結合具體實例形式分析了java基于圖片的讀取、設置、生成等操作實現(xiàn)圖片合并功能的相關實現(xiàn)技巧,需要的朋友可以參考下
    2017-02-02
  • Java編程基于快速排序的三個算法題實例代碼

    Java編程基于快速排序的三個算法題實例代碼

    這篇文章主要介紹了Java編程基于快速排序的三個算法題實例代碼,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下
    2018-01-01
  • Java結構型設計模式之適配器模式詳解

    Java結構型設計模式之適配器模式詳解

    適配器模式,即將某個類的接口轉換成客戶端期望的另一個接口的表示,主要目的是實現(xiàn)兼容性,讓原本因為接口不匹配,沒辦法一起工作的兩個類,可以協(xié)同工作。本文將通過示例詳細介紹適配器模式,需要的可以參考一下
    2022-09-09
  • 關于ArrayList的動態(tài)擴容機制解讀

    關于ArrayList的動態(tài)擴容機制解讀

    這篇文章主要介紹了關于ArrayList的動態(tài)擴容機制解讀,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • Java 添加、讀取和刪除 Excel 批注的操作代碼

    Java 添加、讀取和刪除 Excel 批注的操作代碼

    這篇文章主要介紹了Java 添加、讀取和刪除 Excel 批注的操作方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-04-04
  • Spring Boot處理全局統(tǒng)一異常的兩種方法與區(qū)別

    Spring Boot處理全局統(tǒng)一異常的兩種方法與區(qū)別

    這篇文章主要給大家介紹了關于Spring Boot處理全局統(tǒng)一異常的兩種方法與區(qū)別,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Spring Boot具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-06-06

最新評論