摘要:今天給大家介紹下周期性線程池的使用和重點源碼剖析。用來處理延時任務或定時任務定時線程池類的類結構圖接收類型的任務,是線程池調度任務的最小單位。周期性線程池任務的提交方式周期性有三種提交的方式。
之前學習ThreadPool的使用以及源碼剖析,并且從面試的角度去介紹知識點的解答。今天給大家介紹下周期性線程池的使用和重點源碼剖析。ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor:用來處理延時任務或定時任務
定時線程池類的類結構圖
ScheduledThreadPoolExecutor接收ScheduleFutureTask類型的任務,是線程池調度任務的最小單位。
它采用DelayQueue存儲等待的任務:
1、DelayQueue內部封裝成一個PriorityQueue,它會根據time的先后時間順序,如果time相同則根絕sequenceNumber排序;
2、DelayQueue是無界隊列;
接收的參數:
private final long sequenceNumber;//任務的序號 private long time;//任務開始的時間 private final long period;//任務執行的時間間隔
工作線程的的執行過程:
工作線程會從DelayQueue取出已經到期的任務去執行;
執行結束后重新設置任務的到期時間,再次放回DelayQueue;
ScheduledThreadPoolExecutor會把待執行的任務放到工作隊列DelayQueue中,DelayQueue封裝了一個PriorityQueue,PriorityQueue會對隊列中的ScheduledFutureTask進行排序,具體的排序算法實現如下:
public int compareTo(Delayed other) { if (other == this) // compare zero if same object return 0; if (other instanceof ScheduledFutureTask) { ScheduledFutureTask> x = (ScheduledFutureTask>)other; //首先按照time排序,time小的排到前面,time大的排到后面 long diff = time - x.time; if (diff < 0) return -1; else if (diff > 0) return 1; //time相同,按照sequenceNumber排序; //sequenceNumber小的排在前面,sequenceNumber大的排在后面 else if (sequenceNumber < x.sequenceNumber) return -1; else return 1; } long diff = getDelay(NANOSECONDS) - other.getDelay(NANOSECONDS); return (diff < 0) ? -1 : (diff > 0) ? 1 : 0; }
接下來看看ScheduledFutureTask的run方法實現, run方法是調度task的核心,task的執行實際上是run方法的執行。
public void run() { //是否是周期性的 boolean periodic = isPeriodic(); //線程池是shundown狀態不支持處理新任務,直接取消任務 if (!canRunInCurrentRunState(periodic)) cancel(false); //如果不需要執行執行周期性任務,直接執行run方法結束 else if (!periodic) ScheduledFutureTask.super.run(); //如果需要周期性執行,則在執行任務完成后,設置下一次執行時間 else if (ScheduledFutureTask.super.runAndReset()) { //設置下一次執行該任務的時間 setNextRunTime(); //重復執行該任務 reExecutePeriodic(outerTask); } }
run方法的執行步驟:
1、如果線程池是shundown狀態不支持處理新任務,直接取消任務,否則步驟2;
2、如果不是周期性任務,直接調用ScheduledFutureTask的run方法執行,會設置執行結果,然后直接返回,否則步驟3;
3、如果是周期性任務,調用ScheduledFutureTask的runAndset方法執行,不會設置執行結果,然后直接返回,否則執行步驟4和步驟5;
4、計算下一次執行該任務的時間;
5、重復執行該任務;
接下來看下reExecutePeriodic方法的執行步驟:
void reExecutePeriodic(RunnableScheduledFuture> task) { if (canRunInCurrentRunState(true)) { super.getQueue().add(task); if (!canRunInCurrentRunState(true) && remove(task)) task.cancel(false); else ensurePrestart(); } }
由于已經執行過一次周期性任務,所以不會reject當前任務,同時傳入的任務一定是周期性任務。
周期性線程池任務的提交方式周期性有三種提交的方式:schedule、sceduleAtFixedRate、schedlueWithFixedDelay。下面從使用和源碼兩個方面進行說明,首先是如果提交任務:
pool.schedule(new Runnable() { @Override public void run() { System.out.println("延遲執行"); } },1, TimeUnit.SECONDS); /** * 這個執行周期是固定,不管任務執行多長時間,每過3秒中就會產生一個新的任務 */ pool.scheduleAtFixedRate(new Runnable() { @Override public void run() { //這個業務邏輯需要很長的時間,超過了3秒 System.out.println("重復執行"); } },1,3,TimeUnit.SECONDS); pool.shutdown(); /** * 假如run方法30min后執行完成,然后間隔3秒,再周期性執行下一個任務 */ pool.scheduleWithFixedDelay(new Runnable() { @Override public void run() { //30min System.out.println("重復執行"); } },1,3,TimeUnit.SECONDS);
知道了如何提交周期性任務,接下來源碼是如何執行的,首先是schedule方法,該方法是指任務在指定延遲時間到達后觸發,只會執行一次。
public ScheduledFuture> schedule(Runnable command, long delay, TimeUnit unit) { if (command == null || unit == null) throw new NullPointerException(); //把任務封裝成ScheduledFutureTask,之后調用decorateTask進行包裝; //decorateTask方法是空方法,留給用戶去實現的; RunnableScheduledFuture> t = decorateTask(command, new ScheduledFutureTask(command, null, triggerTime(delay, unit))); //包裝好任務之后,進行任務的提交 delayedExecute(t); return t; }
任務提交方法:
private void delayedExecute(RunnableScheduledFuture> task) { //如果線程池不是RUNNING狀態,則使用拒絕策略把提交任務拒絕掉 if (isShutdown()) reject(task); else { //與ThreadPoolExecutor不同,這里直接把任務加入延遲隊列 super.getQueue().add(task); //如果當前狀態無法執行任務,則取消 if (isShutdown() && !canRunInCurrentRunState(task.isPeriodic()) && remove(task)) task.cancel(false); else //和ThreadPoolExecutor不一樣,corePoolSize沒有達到會增加Worker; //增加Worker,確保提交的任務能夠被執行 ensurePrestart(); } }
還沒關注我的公眾號?
掃文末二維碼關注公眾號【小強的進階之路】可領取如下:
學習資料: 1T視頻教程:涵蓋Javaweb前后端教學視頻、機器學習/人工智能教學視頻、Linux系統教程視頻、雅思考試視頻教程;
100多本書:包含C/C++、Java、Python三門編程語言的經典必看圖書、LeetCode題解大全;
軟件工具:幾乎包括你在編程道路上的可能會用到的大部分軟件;
項目源碼:20個JavaWeb項目源碼。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/76284.html
摘要:一使用線程池的好處線程池提供了一種限制和管理資源包括執行一個任務。每個線程池還維護一些基本統計信息,例如已完成任務的數量。通過重復利用已創建的線程降低線程創建和銷毀造成的消耗。使用無界隊列作為線程池的工作隊列會對線程池帶來的影響與相同。 歷史優質文章推薦: Java并發編程指南專欄 分布式系統的經典基礎理論 可能是最漂亮的Spring事務管理詳解 面試中關于Java虛擬機(jvm)的問...
摘要:時,標準類庫添加了,作為對型線程池的實現。類圖用來專門定義型任務完成將大任務分割為小任務以及合并結果的工作。 JDK 1.7 時,標準類庫添加了 ForkJoinPool,作為對 Fork/Join 型線程池的實現。Fork 在英文中有 分叉 的意思,而 Join 有 合并 的意思。ForkJoinPool 的功能也是如此:Fork 將大任務分叉為多個小任務,然后讓小任務執行,Join...
摘要:子線程往消息隊列發送消息,并且往管道文件寫數據,主線程即被喚醒,從管道文件讀取數據,主線程被喚醒只是為了讀取消息,當消息讀取完畢,再次睡眠。因此的循環并不會對性能有過多的消耗。 說下你所知道的設計模式與使用場景 a.建造者模式: 將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。 使用場景比如最常見的AlertDialog,拿我們開發過程中舉例,比如Camera...
摘要:私有專區價格文檔中的價格以北京二可用區為例,其余可用區的價格可咨詢您的客戶經理。資源池資源池支持按月以及按年付費暫不支持按需付費。價格列表如下核內存磁盤定價元月定價元年私有專區主機私有專區主機生命周期與資源池一致,無收費項。實時文檔歡迎訪問私有專區價格文檔中的價格以北京二可用區E為例,其余可用區的價格可咨詢您的客戶經理。1. 資源池資源池支持按月以及按年付費(暫不支持按需付費)。價格列表如下...
閱讀 1808·2021-11-24 10:21
閱讀 1219·2021-09-22 15:25
閱讀 3178·2019-08-30 15:55
閱讀 718·2019-08-30 15:54
閱讀 3468·2019-08-30 14:20
閱讀 1666·2019-08-30 14:06
閱讀 646·2019-08-30 13:11
閱讀 3155·2019-08-29 16:43