您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關定時任務ScheduledExecutorService實現是怎樣的,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
現在項目中基本不會自己定義一個ScheduledExecutorService來執行定時任務。都是用第三方框架比如xxl-job之類的,不過了解最基本的定時任務原理還是很有必要的。
我們先來看最簡單的使用,代碼如下圖:
ScheduledThreadPoolExecutor是ScheduledExecutorService的實現,能做一些基本的定時任務功能。
比如上圖的例子,可以延遲3秒后每隔5秒執行任務。實現起來比較簡單。
首先ScheduledThreadPoolExecutor繼承了ThreadPoolExecutor并實現了ScheduledExecutorService,所以它擁有線程池的功能。
然后我們看它的構造方法,它有幾個構造方法,我們以上面示例的為例,方法中只有一行代碼如下:
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());
調用了父類的構造方法,也就是線程池的構造方法,它自身并沒有實現任務事情,除了new了一個DelayedWorkQueue()給父類,這個是給線程池存放任務隊列的。
ScheduledThreadPoolExecutor提供了幾個執行定時任務的方法,不過我們還是分析上面用到的scheduleAtFixedRate方法,查看源碼如下圖:
這個方法一共就三步:
首先初始化ScheduledFutureTask,它繼承了FutureTask,擴展了一些定時任務需要的屬性,比如下次執行時間、每次任務執行間隔。
然后調用decorateTask方法裝飾任務,目前沒有做任務事情,我們可以自定義實現一些功能。
最后調用delayedExecute方法,從上圖可以看到這個方法主要流程也簡單,首先是把任務放到線程池的隊列中,然后調用ensurePrestart方法,ensurePrestart方法是線程池的方法,作用是根據線程池線程數調用addWorker方法創建線程,addWorker方法就不多說了,不清楚的同學可以看我前面幾篇的《三分鐘弄懂線程池執行過程》。
提交定時任務的方法還是很簡單的,包裝一個任務(用于保存定時任務需要的信息),然后提交到線程池的隊列中。
但是有一個問題是,任務提交到線程池了,我們知道線程池能夠執行任務,但是都是執行一次啊,不會像定時任務那樣執行任務的!那它到底是怎么實現定時任務功能的呢?
從之前文章分析線程池中最終執行的是提交的任務那個對象的run方法,所以ScheduledFutureTask對run方法有特殊的實現,跟進源碼發現run方法正常的流程就兩步:
調用ScheduledFutureTask.super.runAndReset()也就是調用了父類的FutureTask的runAndReset()方法,這個方法沒有什么在上上篇文章有講,就是調用內部的Callable的run,保存結果;
在上一個執行完成也就是任務完成后,調用了setNextRunTime();reExecutePeriodic(outerTask);兩步代碼,第一步是計算下次任務執行時間,第二個是把任務放到隊列中;
run方法執行了具體的方法,在任務執行成功后重新計算了任務下次執行的時間,并再次把任務加載到了隊列中,重復執行。
但是到目前為止也只是實現了規定了在什么時候執行與重復執行,那么到底是如何實現在具體的時間執行呢?
通過還是沒有發現如何實現定時執行的,沒辦法只能繼續跟進代碼,最后跟到線程池的runWorker方法,在獲取Worker的firstTask方法來執行,想起剛剛在調用ensurePrestart方法中調用addWorker方法傳遞的firstTask都是null,也就是說Worker執行的任務都是從隊列中拉取的任務,他們都沒有自己的firstTask。
也就想起了在初始化方法給線程池傳遞的隊列是new DelayedWorkQueue();所以應該基本找到問題了,我們來看隊列的take方法:
從源碼可以看到是根據隊列中第一個元素,然后利用ScheduledFutureTask的getDelay方法來判斷是否返回任務或者阻塞,getDelay是根據屬性time(前面提到的下次任務執行時間)減去當前時間。
那么如果第一個任務進來判斷是10秒后執行,第二個任務進來判斷是5秒后執行,那么第二個任務是不是等第一個任務執行完才能執行呢?
很明顯不能這么實現,這里就要看DelayedWorkQueue的add方法了,它并沒有實現add方法,不過add方法調用的offer,DelayedWorkQueue實現了offer方法。
offer方法也簡單,就是把任務加到queue數組中,如果數組為null則直接加進去,并喚醒take哪里的線程,如果不為null則比較數組中任務的time,越近的越在前面,如果新加進來的任務被設置到了數組的第一個,則喚醒take那里阻塞的線程。
ScheduledExecutorService繼承線程池,也是把任務提交給線程池執行,只不過它的任務類進行擴展。
任務類ScheduledFutureTask繼承FutureTask并擴展了一些屬性來記錄任務下次執行時間和每次執行間隔。同時重寫了run方法重新計算任務下次執行時間,并把任務放到線程池隊列中。
ScheduledExecutorService自定義了阻塞隊列DelayedWorkQueue給線程池使用,它可以根據ScheduledFutureTask的下次執行時間來阻塞take方法,并且新進來的ScheduledFutureTask會根據這個時間來進行排序,最小的最前面。
總結起來就是通過線程池來執行任務,ScheduledFutureTask記錄任務定時信息,DelayedWorkQueue來排序任務定時執行。
關于定時任務ScheduledExecutorService實現是怎樣的就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。