Java DelayQueue:實現延遲任務的線程安全隊列

一、DelayQueue的概述

Java的DelayQueue 是一個阻塞隊列隊列,主要用來實現對延遲任務的調度,也就是在指定的時間之後才能夠取出任務來執行。該隊列中保存的元素都必須實現了Delayed介面。

在實際應用中,DelayQueue被廣泛地使用於緩存系統中,例如緩存的對象需要在過期時間到達後才能進行刪除。通過使用DelayQueue隊列,不必在添加緩存對象時設置過期時間,只需要在緩存對象失效後將其放入DelayQueue中,等待其到期後再執行對其的刪除操作。

二、DelayQueue的基本使用

下面是一個基本使用的例子:

import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

public class DelayQueueExample {

    static class DelayedElement implements Delayed {
        private long delayTime;     // 存放延遲時間
        private String element;    // 存放實際的任務元素

        DelayedElement(long delay, String element) {
            this.delayTime = System.currentTimeMillis() + delay;
            this.element = element;
        }

        // 獲取剩餘時間
        @Override
        public long getDelay(TimeUnit unit) {
            return unit.convert(delayTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
        }

        // 按剩餘時間進行排序
        @Override
        public int compareTo(Delayed o) {
            return Long.compare(this.getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS));
        }

        // 執行任務
        public void exec() {
            System.out.println("執行任務:" + element);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        DelayQueue queue = new DelayQueue();

        queue.add(new DelayedElement(2000, "任務1"));
        queue.add(new DelayedElement(5000, "任務2"));
        queue.add(new DelayedElement(3000, "任務3"));
        queue.add(new DelayedElement(4000, "任務4"));

        while (!queue.isEmpty()) {
            DelayedElement element = queue.take();
            element.exec();
        }
    }
}

在上述代碼中,我們創建了一個DelayQueue隊列,並向其中添加了四個基於Delayed介面的對象,設置它們的延遲時間分別為2秒、5秒、3秒和4秒。

接下來,我們使用while循環,不斷從隊列中取出任務,若隊列為空,則一直休眠直到隊列中有元素出現。當任務取出後,調用exec()方法執行任務。

三、DelayQueue的特性說明

1. 延遲任務排序

對於DelayQueue中保存的元素,隊列會按照剩餘延遲時間進行排序,以保證最先到期的任務最先被執行。當我們使用poll()、take()等方法從隊列中獲取元素時,都會取出剩餘時間最小的元素,如果延遲時間相同,則取出放入到隊列中時間最長的元素。

2. 線程安全性

DelayQueue為線程安全的隊列,可以被多個線程安全地操作。我們可以通過add()、offer()、put()、take()等方法對DelayQueue進行操作,這些方法都被設計為同步方法,具有互斥的特點。

3. DelayQueue其他方法

除了基本使用中的offer()、poll()、peek()、remove()、take()外,DelayQueue還提供了一些其他方法:

  • drainTo():從隊列中取出多個元素,並將它們放入給定集合中
  • drainTo():從隊列中取出所有元素,並將它們放入給定集合中
  • remainingCapacity():返回隊列剩餘容量

四、DelayQueue的使用實例:訂單超時取消

一個典型的使用場景是:我們在訂單系統中需要對訂單過期時間進行檢查,到了過期時間即可取消訂單。我們可以創建如下的訂單類:

public class Order implements Delayed {
    private String orderId;
    private long expireTime;

    public Order(String orderId, long expireTime) {
        this.orderId = orderId;
        this.expireTime = expireTime;
    }

    // 計算剩餘過期時間
    @Override
    public long getDelay(TimeUnit unit) {
        return expireTime - System.currentTimeMillis();
    }

    @Override
    public int compareTo(Delayed o) {
        return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
    }

    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }
}

在這個Order類中,我們需要實現Delayed介面,並覆蓋getDelay()方法,計算剩餘過期時間,以便進行排序。我們可以為它添加如下方法:

public class Order implements Delayed {
    // 省略getDelay方法和compareTo方法
 
    // 取消訂單
    public void cancel() {
        System.out.println("訂單[" + orderId + "]已自動取消!");
    }
}

接下來我們通過如下的代碼創建DelayQueue,向其中添加訂單並檢測過期時間:

public class OrderCheckTask implements Runnable {
    private DelayQueue queue;

    public OrderCheckTask(DelayQueue queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            // 檢測訂單過期並取消過期訂單
            while (true) {
                Order order = queue.take();
                order.cancel();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 訂單下單,加入隊列
    public boolean order(Order order) {
        return queue.add(order);
    }
}

public class AppMain {
    
    public static void main(String[] args) throws InterruptedException {
        // 創建DelayQueue隊列
        DelayQueue queue = new DelayQueue();
        // 啟動訂單檢測任務
        new Thread(new OrderCheckTask(queue)).start();

        // 添加訂單
        queue.add(new Order("A20191000", System.currentTimeMillis() + 5000));
        queue.add(new Order("A20191001", System.currentTimeMillis() + 4000));
        queue.add(new Order("A20191002", System.currentTimeMillis() + 6000));
        queue.add(new Order("A20191003", System.currentTimeMillis() + 3000));

        // 等待5秒後取消訂單
        Thread.sleep(5000);
        queue.add(new Order("A20191000", System.currentTimeMillis()));

        // 等待15秒輸出隊列大小
        Thread.sleep(15000);
        System.out.println("隊列大小:" + queue.size());
    }
}

在上述代碼中,我們首先創建了DelayQueue隊列和一個訂單檢測任務。在添加訂單時,我們指定延遲時間並加入DelayQueue隊列中。運行程序後,會等待5秒後,通過訂單ID刪除訂單,等待15秒後輸出DelayQueue隊列中還剩餘多少個元素。

五、DelayQueue的總結

DelayQueue主要是用來實現對延遲任務的調度,按剩餘時間排序,線程安全。我們在訂單過期檢測、緩存刪除等場景中都可以使用它。

DelayQueue的實現使用簡單,但需要我們實現Delayed介面中的getDelay()方法和compareTo()方法,並在不同的場景下調整它們的排序方式。使用好DelayQueue,將會大大提高程序處理效率。

原創文章,作者:SGDWA,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/371837.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
SGDWA的頭像SGDWA
上一篇 2025-04-23 18:08
下一篇 2025-04-23 18:08

相關推薦

  • Git secbit:一種新型的安全Git版本

    Git secbit是一種新型的安全Git版本,它在保持Git原有功能的同時,針對Git存在的安全漏洞做出了很大的改進。下面我們將從多個方面對Git secbit做詳細地闡述。 一…

    編程 2025-04-29
  • Java任務下發回滾系統的設計與實現

    本文將介紹一個Java任務下發回滾系統的設計與實現。該系統可以用於執行複雜的任務,包括可回滾的任務,及時恢復任務失敗前的狀態。系統使用Java語言進行開發,可以支持多種類型的任務。…

    編程 2025-04-29
  • Python中的隊列定義

    本篇文章旨在深入闡述Python中隊列的定義及其應用,包括隊列的定義、隊列的類型、隊列的操作以及隊列的應用。同時,我們也會為您提供Python代碼示例。 一、隊列的定義 隊列是一種…

    編程 2025-04-29
  • RabbitMQ和Yii2的消息隊列應用

    本文將探討RabbitMQ和Yii2之間的消息隊列應用。從概念、安裝和配置、使用實例等多個方面詳細講解,幫助讀者了解和掌握RabbitMQ和Yii2的消息隊列應用。 一、Rabbi…

    編程 2025-04-29
  • Saturn 定時任務用法介紹

    本文將從以下幾個方面對Saturn定時任務進行詳細的闡述: 一、Saturn 定時任務簡介 Saturn是一個分散式任務調度系統,支持在線添加、修改定時任務,支持多種任務類型,如J…

    編程 2025-04-29
  • Python線程等待指南

    本文將從多個方面詳細講解Python線程等待的相關知識。 一、等待線程結束 在多線程編程中,經常需要等待線程執行完畢再進行下一步操作。可以使用join()方法實現等待線程執行完畢再…

    編程 2025-04-29
  • Python兩個線程交替列印1到100

    這篇文章的主題是關於Python多線程的應用。我們將會通過實際的代碼,學習如何使用Python兩個線程交替列印1到100。 一、創建線程 在Python中,我們可以使用Thread…

    編程 2025-04-28
  • ROS線程發布消息異常解決方法

    針對ROS線程發布消息異常問題,我們可以從以下幾個方面進行分析和解決。 一、檢查ROS代碼是否正確 首先,我們需要檢查ROS代碼是否正確。可能會出現的問題包括: 是否正確初始化RO…

    編程 2025-04-28
  • 手機安全模式怎麼解除?

    安全模式是一種手機自身的保護模式,它會禁用第三方應用程序並使用僅限基本系統功能。但有時候,安全模式會使你無法使用手機上的一些重要功能。如果你想解除手機安全模式,可以嘗試以下方法: …

    編程 2025-04-28
  • 如何在dolphinscheduler中運行chunjun任務實例

    本文將從多個方面對dolphinscheduler運行chunjun任務實例進行詳細的闡述,包括準備工作、chunjun任務配置、運行結果等方面。 一、準備工作 在運行chunju…

    編程 2025-04-28

發表回復

登錄後才能評論