完整實現-通過DelayQueue實現延時任務

来源:https://www.cnblogs.com/zimug/archive/2022/08/18/16598544.html
-Advertisement-
Play Games

實現延時任務有很多的方法,網上關於延時任務的實現的文章已經不少了。比如:實現延時任務的10種方法等等。但是這些文章基本上都是將方法大概的列舉一下,給出部分示例代碼,對於有經驗的老程式員可能一看就知道該怎麼去把它實現完整,但是對於初學者來說不夠友好。所以,我打算寫一個系列的文章,詳細的給出每種延時任務 ...


實現延時任務有很多的方法,網上關於延時任務的實現的文章已經不少了。比如:實現延時任務的10種方法等等。但是這些文章基本上都是將方法大概的列舉一下,給出部分示例代碼,對於有經驗的老程式員可能一看就知道該怎麼去把它實現完整,但是對於初學者來說不夠友好。所以,我打算寫一個系列的文章,詳細的給出每種延時任務的實現方法、完整實現代碼,以及工作原理,歡迎並期待大家關註我

小概念:什麼是延時任務?舉個例子:你買了一張火車票,必須在30分鐘之內付款,否則該訂單被自動取消。訂單30分鐘不付款自動取消,這個任務就是一個延時任務。

一、DelayQueue的應用原理

DelayQueue是一個無界的BlockingQueue的實現類,用於放置實現了Delayed介面的對象,其中的對象只能在其到期時才能從隊列中取走。

  • BlockingQueue即阻塞隊列,java提供的面向多線程安全的隊列數據結構,當隊列內元素數量為0的時候,試圖從隊列內獲取元素的線程將被阻塞或者拋出異常。
  • 這裡的“無界”隊列,是指隊列的元素數量不存在上限,隊列的容量會隨著元素數量的增加而擴容。


DelayQueue實現了BlockingQueue介面,所以具有無界、阻塞的特點,除此之外它自己的核心特點就是:

  • 放入該隊列的延時任務對象,只要到達延時時間之後才能被取到
  • DelayQueue 不接收null元素
  • DelayQueue 只接受那些實現了java.util.concurrent.Delayed介面的對象

二、訂單延時任務的實現

瞭解了DelayQueue的特點之後,我們就可以利用它來實現延時任務了,實現java.util.concurrent.Delayed介面。

import org.jetbrains.annotations.NotNull;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * 延時訂單任務
 */
public class OrderDelayObject implements Delayed {
  private String name;
  private long delayTime;   //延時時間
  //實際業務中這裡傳訂單信息對象,我這裡只做demo,所以使用字元串了
  private String order;

  public OrderDelayObject(String name, long delayTime, String order) {
    this.name = name;
    //延時時間加上當前時間
    this.delayTime = System.currentTimeMillis() + delayTime;
    this.order = order;
  }

  //獲取延時任務的倒計時時間
  @Override
  public long getDelay(TimeUnit unit) {
    long diff = delayTime - System.currentTimeMillis();
    return unit.convert(diff, TimeUnit.MILLISECONDS);
  }

  //延時任務隊列,按照延時時間元素排序,實現Comparable介面
  @Override
  public int compareTo(@NotNull Delayed obj) {
    return Long.compare(this.delayTime, ((OrderDelayObject) obj).delayTime);
  }

  @Override
  public String toString() {
    Date date = new Date(delayTime);
    SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    return "\nOrderDelayObject:{"
            + "name=" + name
            + ", time=" + sd.format(date)
            + ", order=" + order
            + "}";
  }
} 
  • 上文類中的order為訂單信息對象,在實際的業務開發過程中應該是傳遞訂單信息,用於取消訂單業務的實現(訂單30分鐘不付款自動取消)。
  • Delayed介面繼承自 Comparable介面,所以需要實現compareTo方法,用於延時任務在隊列中按照“延時時間”進行排序。
  • getDelay方法是Delayed介面方法,實現該方法提供獲取延時任務的倒計時時間

三、訂單處理

首先我們需要一個容器,永久保存延時任務隊列,如果是Spring開發環境我們可以這樣做。

@Bean("orderDelayQueue")
public DelayQueue<OrderDelayObject> orderDelayQueue(){
    return new DelayQueue<OrderDelayObject>();
}

當用戶下單的時候,將訂單下單任務放入延時隊列

@Resource
private DelayQueue<OrderDelayObject> orderDelayQueue;

//發起訂單下單的時候將訂單演示對象放入orderDelayQueue
orderDelayQueue.add(
        new OrderDelayObject(
                "訂單延時取消任務",
                30 * 60 * 1000,    //延時30分鐘
                "延時任務訂單對象信息"
        )
);

系統內開啟一個線程,不斷的從隊列中獲取消息,獲取到之後對延時消息進行處理。DelayQueue的take方法從隊列中獲取延時任務對象,如果隊列元素數量為0,或者沒有到達“延時時間的任務”,該線程會被阻塞。

@Component
public class DelayObjectConsumer  implements InitializingBean {

  @Resource
  private DelayQueue<OrderDelayObject> orderDelayQueue;

  @Override
  public void afterPropertiesSet() throws Exception {
    while (true) {
      OrderDelayObject task = orderDelayQueue.take();
      System.out.println(task.toString());
      System.out.println(task.getOrder());
      //根據order訂單信息,去查詢該訂單的支付信息
      //如果用戶沒有進行支付,將訂單從資料庫中關閉
      //如果訂單併發量比較大,這裡可以考慮非同步或線程池的方式進行處理
    }
  }
}

需要說明的是,這裡的while-true迴圈的延時任務處理時順序執行的,在訂單併發量比較大的時候,需要考慮非同步處理的方式完成訂單的關閉操作。我之前寫作一個SpringBoot的可觀測、易配置的線程池開源項目,可能會對你有幫助,源代碼地址:https://gitee.com/hanxt/zimug-monitor-threadpool
經過我的測試,放入orderDelayQueue的延時任務,在半小時之後得到正確的執行處理。說明我們的實現是正確的。

四、優缺點

使用DelayQueue實現延時任務非常簡單,而且簡便,全部都是標準的JDK代碼實現,不用引入第三方依賴(不依賴redis實現、消息隊列實現等),非常的輕量級。

它的缺點就是所有的操作都是基於應用記憶體的,一旦出現應用單點故障,可能會造成延時任務數據的丟失。如果訂單併發量非常大,因為DelayQueue是無界的,訂單量越大,隊列內的對象就越多,可能造成OOM的風險。所以使用DelayQueue實現延時任務,只適用於任務量較小的情況。
歡迎關註我的公告號:字母哥雜談,回覆003贈送作者專欄《docker修煉之道》的PDF版本,30餘篇精品docker文章。字母哥博客:zimug.com


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、先配置spring、springmvc通用xml文件的頭部信息 spring、springmvc通用xml文件的模板內容如下: 1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework. ...
  • 使用文件流對文件進行複製: package io; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOExce ...
  • K 哥之前寫過一篇關於百度翻譯逆向的文章,也在 bilibili 上出過相應的視頻,最近在 K 哥爬蟲交流群中有群友提出,百度翻譯新增了一個請求頭參數 Acs-Token,如果不攜帶該參數,直接按照以前的方法進行處理,會出現 1022 報錯,並且如果直接將 Acs-Token 寫成定值,前幾次可能能 ...
  • 1、標記跳過 在自動化測試過程中,我們經常會遇到因為某些功能阻塞,未開發完成或者環境等問題,一些測試用例不能執行,如果人工去註釋掉,後面還需要再恢復才能繼續測試,pytest的標記跳過功能就可以實現暫時跳過。 (一)無條件跳過skip 使用方法:通過@pytest.mark.skip(reason= ...
  • 0. 原代碼預覽 簡單實現在資料庫中插入數據 public void testInsert() throws IOException { //獲取核心配置文件的輸入流 InputStream is = Resources.getResourceAsStream("mybatis-config.xml ...
  • 最近在使用海康威視的攝像頭進行車牌抓拍,使用了海康威視設備網路SDK,便稍做包裝,便於項目調用。 項目地址 https://github.com/Mr-LuXiaoHua/hikivision-sdk 海康設備網路SDK版本 CH-HCNetSDKV6.1.9.4_build20220412_win ...
  • 1. Page Cache 1.1 Page Cache 是什麼? 為了理解 Page Cache,我們不妨先看一下 Linux 的文件 I/O 系統,如下圖所示: Figure1. Linux 文件 I/O 系統 上圖中,紅色部分為 Page Cache。可見 Page Cache 的本質是由 L ...
  • 兄弟們,現在短視頻主播好看的妹子太多了,有時候遇到自己喜歡的,雖然點了贊或者收藏了,但是萬一主播把視頻隱藏下架了呢? 所以今天咱們就用Python來把這些好看的視頻統統保存下來… 涉及知識點 1、動態數據抓包演示 2、json數據解析方法 3、視頻數據保存 環境介紹 python 3.6 pycha ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...