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

Java多線程之等待隊列DelayQueue詳解

 更新時間:2023年12月14日 11:03:15   作者:愛喝咖啡的程序員  
這篇文章主要介紹了Java多線程之等待隊列DelayQueue詳解,    DelayQueue被稱作"等待隊列"或"JDK延遲隊列",存放著實現(xiàn)了Delayed接口的對象,對象需要設置到期時間,當且僅當對象到期,才能夠從隊列中被取走(并非一定被取走),需要的朋友可以參考下

一. 概念

DelayQueue被稱作"等待隊列"或"JDK延遲隊列",存放著實現(xiàn)了Delayed接口的對象。對象需要設置到期時間,當且僅當對象到期,才能夠從隊列中被取走(并非一定被取走)。DelayQueue的內部使用了PriorityQueue來存放元素,需要元素實現(xiàn)Comparable接口,優(yōu)先級隊列會根據(jù)對象的到期時間實現(xiàn)有序排序。

二. 案例

本案例參考了《Java編程思想》第21章P726頁的例子。

1. 定義延遲任務對象

class DelayedTask implements Runnable, Delayed { // Delayed接口必須實現(xiàn),Runnable接口可以不實現(xiàn)
    private static int counter = 0;
    private final int id = counter++;
    /**
     * 延遲的時間(單位: 毫秒)
     */
    private final int delta;
    /**
     * 任務準備執(zhí)行的時間點(單位: 納秒)
     */
    private final long trigger;
    protected static List<DelayedTask> sequence = new ArrayList<>();
    public DelayedTask(int delayInMilliseconds) {
        delta = delayInMilliseconds;
        trigger = System.nanoTime() + NANOSECONDS.convert(delta, MILLISECONDS);
        sequence.add(this);
    }
    @Override
    public long getDelay(@NotNull TimeUnit unit) {
        // 過期時間
        return unit.convert(trigger - System.nanoTime(), NANOSECONDS);
    }
    /**
     * 用于在過期任務隊列中,任務之間執(zhí)行順序的排序
     */
    @Override
    public int compareTo(@NotNull Delayed arg) {
        DelayedTask that = (DelayedTask)arg;
        if(trigger < that.trigger) return -1;
        if(trigger > that.trigger) return 1;
        return 0;
    }
    @Override
    public void run() {
        System.out.println(this + " ");
    }
    @Override
    public String toString() {
        // 語法規(guī)則: %[argument_index$][flags][width][.precision]conversion
        // %后面的1$指的是第一個參數(shù),也就是delta
        // $后面的-4d指的是如果數(shù)據(jù)總長度不夠4位,則由右向左補足空格
        return String.format("[%1$-4d]", delta) + " Task " + id;
    }
    public String summary() {
        return "(" + id + ":" + delta + ")";
    }
    public static class EndSentinel extends DelayedTask {
        private ExecutorService exec;
        public EndSentinel(int delay, ExecutorService e) {
            super(delay);
            exec = e;
        }
        @Override
        public void run() {
            for(DelayedTask pt : sequence) {
                System.out.println(pt.summary() + " ");
            }
            System.out.println();
            System.out.println(this + " Calling shutdownNow()");
            exec.shutdownNow();
        }
    }
}

2. 定義隊列的消費者

class DelayedTaskConsumer implements Runnable {
    private DelayQueue<DelayedTask> q;
    public DelayedTaskConsumer(DelayQueue<DelayedTask> q) {
        this.q = q;
    }
    @Override
    public void run() {
        try {
            while(!Thread.interrupted()) {
                // DelayQueue take()
                // 取出隊列中的head元素,若隊列中沒有任何延遲到期的元素存在,則該方法將會被阻塞
                // 此時有兩種可能: 1. 隊列中沒有元素 2. 隊列中有元素,但都尚未過期
                // 直到隊列中有延遲到期的元素為止
                // 即便是不讓DelayedTask實現(xiàn)Runnable接口,本例的執(zhí)行結果也不會發(fā)生改變。
                // 因為此處直接調用了run()方法,并沒有分配其他線程去驅動DelayedTask
                q.take().run();
            }
        } catch (InterruptedException e) {
            // Acceptable way to exit
            System.out.println("Acceptable way to exit");
        }
        System.out.println("Finished DelayedTaskConsumer");
    }
}

3. main方法

public class DelayQueueDemo {
    public static void main(String[] args) {
        test2();
    }
    public static void test1() {
        Random random = new Random(47);
        ExecutorService exec = Executors.newCachedThreadPool();
        DelayQueue<DelayedTask> queue = new DelayQueue<>();
        // Fill with tasks that have random delays:
        for(int i = 0; i < 5; i++) {
            queue.put(new DelayedTask(random.nextInt(5000)));
        }
        //使用DelayQueue take()的方式獲取任務
        queue.add(new DelayedTask.EndSentinel(5000, exec));
        exec.execute(new DelayedTaskConsumer(queue));
    }
    public static void test2() {
        Random random = new Random(47);
        ExecutorService exec = Executors.newCachedThreadPool();
        DelayQueue<DelayedTask> queue = new DelayQueue<>();
        // Fill with tasks that have random delays:
        for(int i = 0; i < 5; i++) {
            queue.put(new DelayedTask(random.nextInt(5000)));
        }
        //使用DelayQueue poll()方式獲取任務
        while(queue.size() != 0) {
            // 每執(zhí)行一次DelayQueue poll()且返回的元素不是null,則DelayQueue等待隊列的元素個數(shù)會減一
            DelayedTask task = queue.poll();
            if(task != null) {
                System.out.println(LocalDateTime.now(ZoneId.of("+08:00")));
            }
        }
    }
}

由于案例中給Random設置了種子,因此過期時間的值是可以預測的(每次執(zhí)行都保持一致)。test1()的執(zhí)行結果如下:

 * [555 ] Task 1
 * [961 ] Task 4
 * [1693] Task 2
 * [1861] Task 3
 * [4258] Task 0
 * (0:4258)
 * (1:555)
 * (2:1693)
 * (3:1861)
 * (4:961)
 * (5:5000)

其中,[]方括號代表任務的執(zhí)行順序,()代表任務的創(chuàng)建順序。顯而易見,任務的創(chuàng)建順序與執(zhí)行順序沒有任何關系,任務嚴格按照延遲時間的長短運行。

三. 總結

1. 延遲隊列中的對象只有到期后才能夠從隊列中被取走。(若沒有到期或隊頭元素為null,則DelayQueue會陷入阻塞,說白了就是循環(huán)等待,可以參考DelayQueue的take()方法,寫了一個for(;;) )

2. 對象并非到期后就會被立刻取走,每次取出(poll())的僅僅是到期元素中隊頭元素。

3. 任務的創(chuàng)建順序與任務的執(zhí)行順序沒有任何關系,延遲隊列中任務的排序順序與過期任務對Comparable接口compareTo()方法的具體實現(xiàn)有關。

四. 疑問

還是以下圖為例

若在插入c之前,時間過去了100納秒,c仍然應該排列在b之前嗎?照此推論,只要把過期時長控制在500納秒以內,所有插入的任務都應該在b之前執(zhí)行(因為過期時長比b短),這是不是非常不合理?既然時間過去了100納秒,為什么不將a的過期時長改變成900納秒,b的過期時長改變成400納秒,最后讓b在c之前執(zhí)行呢?

遺憾的是,在DelayQueue的源碼中,并沒有看到對容器PriorityQueue內部元素有做任何定時器,試圖改變任務過期時長的代碼。

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

相關文章

  • Maven jar包沖突的解決方案

    Maven jar包沖突的解決方案

    這篇文章主要介紹了Maven jar包沖突的解決方案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-04-04
  • java實現(xiàn)Redisson看門狗機制

    java實現(xiàn)Redisson看門狗機制

    redission看門狗機制是解決分布式鎖的續(xù)約問題,本文就來詳細的介紹一下java實現(xiàn)Redisson看門狗機制,具有一定的參考價值,感興趣的可以了解一下
    2024-09-09
  • Spring?Security實現(xiàn)添加圖片驗證功能

    Spring?Security實現(xiàn)添加圖片驗證功能

    這篇文章主要為大家介紹了Spring?Security實現(xiàn)添加圖片驗證功能詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01
  • Java實現(xiàn)讀取鍵盤輸入保存到txt文件,再統(tǒng)計并輸出每個單詞出現(xiàn)次數(shù)的方法

    Java實現(xiàn)讀取鍵盤輸入保存到txt文件,再統(tǒng)計并輸出每個單詞出現(xiàn)次數(shù)的方法

    這篇文章主要介紹了Java實現(xiàn)讀取鍵盤輸入保存到txt文件,再統(tǒng)計并輸出每個單詞出現(xiàn)次數(shù)的方法,涉及java文件I/O操作及字符串遍歷、運算實現(xiàn)統(tǒng)計功能相關技巧,需要的朋友可以參考下
    2017-07-07
  • SpringBoot項目接入Nacos的實現(xiàn)步驟

    SpringBoot項目接入Nacos的實現(xiàn)步驟

    SpringBoot項目使用nacos作為配置中心和服務注冊中心,同時兼容dubbo的注冊中心。 本Demo項目使用的SpringBoot版本是2.3.9.RELEASE
    2021-05-05
  • Java枚舉(enum) 詳解7種常見的用法

    Java枚舉(enum) 詳解7種常見的用法

    這篇文章主要介紹了Java枚舉(enum) 詳解7種常見的用法,具有一定的參考價值,有需要的可以了解一下。
    2016-11-11
  • Java?靜態(tài)代理與動態(tài)代理解析

    Java?靜態(tài)代理與動態(tài)代理解析

    這篇文章主要介紹了Java?靜態(tài)代理與動態(tài)代理解析,關于靜態(tài)代理與動態(tài)代理,一直是比較困擾很多新人開發(fā),但實際我們開發(fā)中,小到寫的某個工具類,大到經常使用的Retrofit?其內部都使用了動態(tài)代理,所以這篇文章從基礎到源碼解析,以便簡單理解靜態(tài)代理與Jdk中的動態(tài)代理
    2022-02-02
  • SpringMVC獲取請求參數(shù)實現(xiàn)方法介紹

    SpringMVC獲取請求參數(shù)實現(xiàn)方法介紹

    Spring MVC 是 Spring 提供的一個基于 MVC 設計模式的輕量級 Web 開發(fā)框架,本質上相當于 Servlet,Spring MVC 角色劃分清晰,分工明細,這篇文章主要介紹了SpringMVC實現(xiàn)獲取請求參數(shù)方法
    2022-11-11
  • Spring AI 入門學習指南

    Spring AI 入門學習指南

    本文介紹了如何使用SpringAI和Ollama進行AI開發(fā),包括如何配置和使用聊天、圖像、語音轉文字和文字轉語音模型,以及如何在Java項目中集成Ollama模型,感興趣的朋友一起看看吧
    2024-11-11
  • Java日常練習題,每天進步一點點(31)

    Java日常練習題,每天進步一點點(31)

    下面小編就為大家?guī)硪黄狫ava基礎的幾道練習題(分享)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧,希望可以幫到你
    2021-07-07

最新評論