日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

详解ScheduledExecutorService的周期性执行方法

發布時間:2023/12/20 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 详解ScheduledExecutorService的周期性执行方法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

2019獨角獸企業重金招聘Python工程師標準>>>

詳解 ScheduledExecutorService 的周期性執行方法

在最近的工作中,需要實現一個當一個任務執行完后,再等 100 毫秒然后再次執行的功能。當時最先反映到的就是 java 線程池的 ScheduledExecutorService,而 ScheduledExecutorService 有兩個周期性執行任務的方法,分別是 scheduleAtFixedRate 與 scheduleWithFixedDelay,當時對這兩個方法也不大了解,感覺和我的理解有所偏差,所以對這兩個方法進行了研究。

ScheduledExecutorService 的基本原理

想要了解 scheduleWithFixedDelay 和 scheduleAtFixedRate 這兩個周期性執行任務的方法,首先要了解 ScheduledExecutorService 的原理。在《java 并發編程的藝術》一書中有詳細的解說,這里就簡單的闡述一下。
ScheduledExecutorService 與其他線程池的區別,主要在于在執行前將任務封裝為ScheduledFutureTask與其使用的阻塞隊列DelayedWorkQueue

ScheduledFutureTask

private class ScheduledFutureTask<V>extends FutureTask<V> implements RunnableScheduledFuture<V> {/** 表示這個任務添加到ScheduledExecutorService中的序號 */private final long sequenceNumber;/** T表示這個任務將要被執行的具體時間(時間戳) */private long time;/*** 表示任務執行的間隔周期,若為0則表示不是周期性執行任務*/private final long period;/*省略以下代碼*/}

DelayedWorkQueue

DelayedWorkQueue 是一個優先隊列,在元素出隊時,ScheduledFutureTask 的 time 最小的元素將優先出隊,如果 time 值相同則判斷 sequenceNumber,先入隊的元素先出隊。
而 DelayedWorkQueue 也是 ScheduledExecutorService 能夠定時執行任務的核心類。
首先回顧一下線程池的執行流程:

  • 向線程池提交任務,這時任務將入隊到該線程池的阻塞隊列
  • 工作線程不斷從隊列中取出任務,并執行,若然隊列中沒有任務,工作線程將阻塞直到任務的到來。
  • 當工作線程執行 DelayedWorkQueue 的出隊方法時,DelayedWorkQueue 首先獲取到 time 值最小的 ScheduledFutureTask,即將要最先執行的任務。然后用 time 值(任務要執行的時間戳)與當前時間作比較,判斷任務執行時間是否到期,若然到期,元素立馬出隊,交由工作線程執行。
    但是當 time 值還沒到期呢?那么 time 將會減去當前時間,得到 delay 值(延遲多少時間后執行任務),然后使用方法Condition.awaitNanos(long nanosTimeout),阻塞獲取任務的工作線程,直到經過了 delay 時間,即到達了任務的執行時間,元素才會出隊,交由工作線程執行。

    scheduleAtFixedRate 與 scheduleWithFixedDelay

    根據我之前的理解,認為 scheduleAtFixedRate 是絕對周期性執行,例如間隔周期為 10 秒,那么任務每隔 10 秒都會執行一次,不管任務是否成功執行。但是我的理解是錯誤的,這兩個方法的功能分別是:

  • scheduleAtFixedRate:任務執行完成后,在提交任務到任務執行完成后的時間是否經過了 period,若然經過了,即馬上再次執行該任務。否則等待,直到提交任務到現在已經經過了 period 時間,再次執行該任務。
  • scheduleWithFixedDelay:任務執行完成后,等待 delay 時間,然后再次執行。
  • 要清楚,一個定時任務,不管是否為周期性執行,都將會只由一條工作線程執行

    首先看下這兩個方法的源碼

    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit) {if (command == null || unit == null)throw new NullPointerException();if (period <= 0)throw new IllegalArgumentException();ScheduledFutureTask<Void> sft =new ScheduledFutureTask<Void>(command,null,triggerTime(initialDelay, unit),unit.toNanos(period));RunnableScheduledFuture<Void> t = decorateTask(command, sft);sft.outerTask = t;delayedExecute(t);return t; }public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit) {if (command == null || unit == null)throw new NullPointerException();if (delay <= 0)throw new IllegalArgumentException();ScheduledFutureTask<Void> sft =new ScheduledFutureTask<Void>(command,null,triggerTime(initialDelay, unit),unit.toNanos(-delay));RunnableScheduledFuture<Void> t = decorateTask(command, sft);sft.outerTask = t;delayedExecute(t);return t; }

    其實兩個方法沒有太大區別,只是在構建 ScheduledFutureTask 的時候,ScheduledFutureTask 的 period 屬性有正負差別,scheduleAtFixedRate 方法構建 ScheduledFutureTask 的 period 為負數,而 scheduleWithFixedDelay 為正數。
    接下來查看 ScheduledFutureTask 的 run 方法,工作線程在執行任務時將會調用該方法

    /*** Overrides FutureTask version so as to reset/requeue if periodic.*/ public void run() {boolean periodic = isPeriodic();if (!canRunInCurrentRunState(periodic))cancel(false);//1else if (!periodic)ScheduledFutureTask.super.run();//2else if (ScheduledFutureTask.super.runAndReset()) {//3setNextRunTime();reExecutePeriodic(outerTask);} }

    如果定時任務時周期性執行方法,將會進入到 3 的執行邏輯,當然在這之前將會調用 runAndReset 執行任務邏輯。
    當任務邏輯執行完成后,將會調用 setNextRunTime。

    /*** Sets the next time to run for a periodic task.*/ private void setNextRunTime() {long p = period;if (p > 0)time += p;elsetime = triggerTime(-p); }long triggerTime(long delay) {return now() +((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay)); }

    如果 period 為正數數,即執行的方法為 scheduleAtFixedRate,在任務的執行時間上添加 period 時間。
    而 period 為負數,即執行的方法為 scheduleWithFixedDelay,將 time 改寫為當前時間加上 period 時間。
    執行完 setNextRunTime 方法后,將執行 reExecutePeriodic 方法,即重新將該 ScheduledFutureTask 對象,重新添加到隊列中,等待下一次執行。
    要清楚,不論調用哪個周期性執行方法,都是需要等到任務邏輯執行完成后,才能再次添加到隊列中,等待下一次執行。

    scheduleAtFixedRate 方法,每次都是在 time 的基礎上添加 period 時間,如果任務邏輯的執行時間大于 period,那么在定時任務再次出隊前,time 必定是小于當前時間,馬上出隊被工作線程執行。因為 time 每次都是任務開始執行的時間點。
    scheduleWithFixedDelay 方法,每次都將 time 設置為當前時間加上 period,那么輪到定時任務再次出隊時,必定是經過了 period 時間,才能被工作線程執行。

    總結

    對于 ScheduledExecutorService 一定要清楚,周期性執行任務,一定是等到上一次執行完成后,才能再次執行,即每個任務只由一條線程執行。那么要實現當達到一定時候后,不論任務是否執行完成,都將再次執行任務的功能,ScheduledExecutorService 的兩個周期性執行方法都是不能實現的。其實也就是對于復雜的時間調度控制,ScheduledExecutorService 并不在行。

    轉載于:https://my.oschina.net/bingzhong/blog/1559849

    總結

    以上是生活随笔為你收集整理的详解ScheduledExecutorService的周期性执行方法的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。