簡體   English   中英

Java 庫 class 來處理“回調”的預定執行?

[英]Java library class to handle scheduled execution of “callbacks”?

我的程序有一個組件——稱為調度程序——它允許其他組件注冊它們想要被回調的時間點。 這應該很像 Unix cron 服務,即您告訴調度程序“在每整整一小時后十分鍾通知我”。

我意識到 Java 中沒有真正的回調。

這是我的方法,是否有一個圖書館已經做了這些事情? 也可以隨意提出改進建議。

注冊對調度程序通行證的調用:

  • 包含小時、分鍾、秒、年月、dom、dow 的時間規范,其中每個項目可能未指定,意思是“每小時/分鍾等執行一次”。 (就像 crontabs 一樣)
  • 一個 object 包含的數據將告訴調用 object 在收到調度程序通知時該怎么做。 調度程序不處理這些數據,只是將其存儲並在收到通知時將其傳回。
  • 對調用 object 的引用

在啟動時,或在新的注冊請求之后,調度程序以當前系統時間的日歷 object 開始,並檢查數據庫中是否有任何條目與該時間點匹配。 如果有,則執行它們並重新開始該過程。 如果沒有,日歷 object 中的時間會增加一秒並重新檢查主條目。 如此重復,直到有一個或多個匹配項。 (離散事件模擬)

然后調度程序將記住該時間戳,每秒睡眠和喚醒以檢查它是否已經存在。 如果它碰巧醒來並且時間已經過去,它會重新開始,同樣如果時間到了並且作業已經執行。


編輯:感謝您將我指向 Quartz。 然而,我正在尋找更小的東西。

查找石英

如果您的對象確切地知道它們希望執行的各個時間點,那么您可以使用java.util.concurrent.ScheduledExecutorService 然后他們簡單地調用:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
long timeToExecute = ... //read from DB? use CronTrigger?
long delayToExecution = timeToExecute - System.currentTimeMillis();
scheduler.schedule(aRunnable, delayToExecution, TimeUnit.MILLISECONDS);

如果您希望調度程序本身處理諸如“每 5 秒執行一次”之類的功能,或者您想要圍繞錯過執行的復雜行為,或者執行審計跟蹤的持久性,您只需要使用Quartz

您實際上可以簡單地重復使用 Quartz 的CronTrigger class 來獲得“下一個執行時間”。 class 是完全獨立的,不依賴於從Quartz “上下文”中調用。 一旦您將下一個執行時間設置為Datelong ,您就可以像上面一樣使用 Java ScheduledExecutorService

Quartz是該領域的大而明顯的強者,但也有一些替代品可供探索。

Cron4j是一個足夠好的庫,它比 Quartz 更輕量級。 它提供了良好的文檔,並會做你想做的事。

可能更有趣的是,如果您想使用更適合 Java 並發庫(特別是 Executors 和 ScheduledExecutors)的庫,那么HA-JDBC有一個CronExecutorService接口,由其CronThreadPoolExecutor實現。 現在,有趣的是,它依賴於 Quartz(提供 CronExpression 類),但我發現兩者一起工作比單獨使用 Quartz 更好。 如果您不想要大的依賴關系,可以很容易地從 Quartz 和 HA-JDBC 中提取少量的類來實現這一點。

因為你想要更小的東西(剛剛注意到你的編輯),所以從 Quartz 中獲取 CronExpression,以及我上面提到的兩個 HA-JDBC 類。 這樣就可以了。

如果您的需求很簡單,請考慮使用java.util.Timer

public class TimerDemo {

  public static void main(String[] args) {
    // non-daemon threads prevent termination of VM
    final boolean isDaemon = false;
    Timer timer = new Timer(isDaemon);

    final long threeSeconds = 3 * 1000;
    final long delay = 0;
    timer.schedule(new HelloTask(), delay, threeSeconds);

    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.MINUTE, 1);
    Date oneMinuteFromNow = calendar.getTime();

    timer.schedule(new KillTask(timer), oneMinuteFromNow);
  }

  static class HelloTask extends TimerTask {
    @Override
    public void run() {
      System.out.println("Hello");
    }
  }

  static class KillTask extends TimerTask {

    private final Timer timer;

    public KillTask(Timer timer) {
      this.timer = timer;
    }

    @Override
    public void run() {
      System.out.println("Cancelling timer");
      timer.cancel();
    }
  }

}

如前所述,如果您需要, java.util.concurrentExecutorService提供了更豐富的 API。

我強烈推薦cron4j (已經提到過)而不是 Quartz,除非你絕對需要 Quartz 的一些更高級和復雜的特性。 Cron4j 很好地專注於它應該做的事情,擁有不錯的文檔,並且不是一個廚房水槽解決方案。

可能更有趣的是,如果您想使用更適合 Java 並發庫(特別是 Executors 和 ScheduledExecutors)的庫,那么 HA-JDBC 有一個 CronExecutorService 接口,由其 CronThreadPoolExecutor 實現。 現在,有趣的是,它依賴於 Quartz(提供 CronExpression 類),但我發現兩者一起工作比單獨使用 Quartz 更好。 如果您不想要大的依賴關系,可以很容易地從 Quartz 和 HA-JDBC 中提取少量的類來實現這一點。

我只是想說我嘗試提取這些類,並且成功了:我需要這三個類:

  • CronExpression(石英)
  • CronThreadPoolExecutor (ha-jdbc)
  • DaemonThreadFactory (ha-jdbc)

我只需要做這些小調整:

  • 從 CronThreadPoolExecutor 中刪除記錄器(它已創建但從未使用過)
  • 將常量 YEAR_TO_GIVEUP_SCHEDULING_AT 從 CronTrigger 移動到 CronExpression

我很高興我沒有陷入糾纏不清的依賴關系中。 祝賀 class 作者!

它一直像冠軍一樣工作。

通常推薦使用Quartz 調度器。

不敢相信 java.util.Timer 被選為答案。 石英確實是一個更好的選擇。

與 java.util.Timer 相比,石英的一大優勢在於,使用石英,作業可以存儲在數據庫中。 結果,一個 jvm 可以調度,另一個可以執行。 此外(顯然)該請求在 jvm 重新啟動后仍然存在。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM