- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) >
- Java簡(jiǎn)單實(shí)現定時(shí)器
本文實(shí)例為大家分享了Java簡(jiǎn)單實(shí)現定時(shí)器的具體代碼,供大家參考,具體內容如下
定時(shí)器相當于一個(gè)任務(wù)管理器。有些任務(wù)可能現在執行, 有些任務(wù)可能過(guò)1個(gè)小時(shí),甚至很久才會(huì )執行。定時(shí)器就是對這些任務(wù)進(jìn)行管理監視, 如果一個(gè)任務(wù)執行時(shí)間到了,定時(shí)器就會(huì )將這個(gè)任務(wù)執行。 保證所有的任務(wù)都會(huì )在合適的時(shí)間執行。
對于定時(shí)器的實(shí)現,我們可以劃分為3個(gè)部分。
1、 使用一個(gè)Task類(lèi)描述每一個(gè)任務(wù)(里面包含任務(wù)的執行方法, 定時(shí)時(shí)間)。
2、 使用優(yōu)先級隊列管理這些任務(wù)類(lèi)。
2.1 我們都知道優(yōu)先級隊列底層實(shí)現是堆(以小根堆為例), 堆頂的元素是所有的元素的最小值。 我們以任務(wù)的定時(shí)時(shí)間為比較原則構建, 這樣就可以保證堆頂元素的任務(wù)執行時(shí)間是最短的(這樣的實(shí)現,我們需要在Task類(lèi)內部定義比較規則-即重寫(xiě)Comparable接口的CompareTo方法)。
2.2 當一個(gè)任務(wù)執行完畢, 就會(huì )從優(yōu)先級隊列取出poll掉, 然后內部重新組織保證新的堆頂元素是定時(shí)時(shí)間最短的。
2.3 如果說(shuō)堆頂的任務(wù)定時(shí)時(shí)間還沒(méi)有到達(當然后續的任務(wù)定時(shí)時(shí)間肯定會(huì )更長(cháng),不會(huì )被執行)
3、使用一個(gè)線(xiàn)程循環(huán)掃描優(yōu)先級隊列, 相當于一個(gè)監控線(xiàn)程,循環(huán)判斷堆頂任務(wù)是否滿(mǎn)足執行時(shí)間。
1、制定任務(wù)類(lèi)Task
Task類(lèi)包含任務(wù)的 執行方法 和 定時(shí)時(shí)間。
1.1 執行方法我采用封裝Runnable中run方法實(shí)現, 這樣做是為了后續添加任務(wù)時(shí)方便寫(xiě)執行邏輯。
1.2 定時(shí)時(shí)間就是long類(lèi)型的變量
1.3 制定比較規則, 后續優(yōu)先級隊列中存放的是Task對象(而在內部構建時(shí),需要比較兩個(gè)Task對象的),對于對象的比較, 我們以對象的定時(shí)時(shí)間為規則, 制定小根堆。
static class Task implements Comparable<Task>{ //Runnable類(lèi)中有一個(gè)run方法, 通過(guò)這個(gè)方法實(shí)現任務(wù)的執行 private Runnable command; //time表示執行的時(shí)間 private long time; //構造方法 public Task(Runnable command, long time) { this.command = command; this.time = System.currentTimeMillis() + time; //將時(shí)間轉化為絕對時(shí)間 } //執行任務(wù)的邏輯 public void run() { command.run(); } //定義比較方法 - 方便后續的優(yōu)先級隊列構建 @Override public int compareTo(Task o) { return (int)(this.time - o.time); } }
2、監管線(xiàn)程&定時(shí)器對象Timer
監管線(xiàn)程Worker中包含優(yōu)先級隊列(小根堆)queue 和 循環(huán)監管的流程。
Timer對象封裝了監管線(xiàn)程Woker 和 任務(wù)的添加方法schedule()
關(guān)于監管線(xiàn)程的優(yōu)化
2.1 循環(huán)監控存在一個(gè)弊端,那就是一直循環(huán)判斷, 占用CPU資源。
(假如堆首任務(wù)的執行是1小時(shí)后, 再次期間監管線(xiàn)程會(huì )跑1小時(shí)循環(huán)判斷。)
解決方法: 可以通過(guò)線(xiàn)程阻塞和喚醒來(lái)解決。在下面代碼有詳細注釋和實(shí)現。
2.1.1 如果任務(wù)1小時(shí)后執行, 我們讓監管線(xiàn)程wait(1小時(shí)), 但在此期間如果有新的任務(wù)添加進(jìn)來(lái)(可能新的任務(wù)需要等30分鐘就可以執行,堆首元素發(fā)生變化) ,這時(shí)需要喚醒監管線(xiàn)程來(lái)重新判斷。(由于wait和notify方法不在用一個(gè)類(lèi)中實(shí)現, 我們通過(guò)一個(gè)Object(mailBox)來(lái)阻塞、喚醒)
//檢測線(xiàn)程, 繼承Thread類(lèi),重寫(xiě)內部run方法,屬于線(xiàn)程的創(chuàng )建方法之一。 static class Worker extends Thread { //優(yōu)先級隊列 - JUC包里面 private PriorityBlockingQueue<Task> queue = null; //為了對監管線(xiàn)程進(jìn)行阻塞和喚醒,采用同一對象 private Object mailBox = null; //構造函數 public Worker(PriorityBlockingQueue<Task> queue, Object mailBox) { this.queue = queue; this.mailBox = mailBox; } @Override public void run() { //實(shí)現具體的執行邏輯 while(true) { try { //1、取優(yōu)先級隊列的隊首元素 Task task = queue.peek(); //2、比較隊首的元素的時(shí)間是否大于當前時(shí)間 if(task == null) { continue; } long curTime = System.currentTimeMillis(); if(task.time > curTime) { //時(shí)間還沒(méi)有到, 由于取出了任務(wù), 需要重新放置回去 //優(yōu)化1: 空循環(huán)等待 - wait(time) 讓線(xiàn)程休眠time時(shí)間,然后在執行 // 如果在等待期間有新的任務(wù)添加, 這個(gè)時(shí)候我們喚醒線(xiàn)程, 繼續判斷(因為存在新的時(shí)間過(guò)短需要立即執行) // 這個(gè)只需要添加一個(gè)新任務(wù)時(shí), 喚醒即可 //優(yōu)化2: 訪(fǎng)問(wèn)隊首元素而不是取出, 防止無(wú)所謂的刪除、插入。(維護優(yōu)先級隊列是有消耗的) long gapTime = task.time - curTime; synchronized (mailBox) { mailBox.wait(gapTime); } } else { //直接執行 //如果執行到了, 則會(huì )刪除頭部元素, 調用任務(wù)的執行過(guò)程。 task = queue.take(); task.run(); } } catch(InterruptedException e) { e.printStackTrace(); break; } } } } //定時(shí)器簡(jiǎn)單實(shí)現 static class Timer { //定時(shí)器的實(shí)現步驟 //1、用一個(gè)類(lèi)描述任務(wù) //2、用優(yōu)先級隊列管理這些任務(wù), 比較方法通過(guò)任務(wù)的制定時(shí)間,每次取隊首元素 // 隊首元素是執行時(shí)間最近的 private PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>(); //3、用一個(gè)線(xiàn)程來(lái)循環(huán)掃描當前的阻塞隊列,判斷隊首的執行時(shí)間, 如果執行時(shí)間到了,那就執行。 //4、創(chuàng )建一個(gè)Object對象,用于設置線(xiàn)程阻塞使用的, 存在線(xiàn)程阻塞, 添加任務(wù)時(shí)喚醒的操作 private Object mailBox = new Object(); //構造函數 public Timer() { //創(chuàng )建線(xiàn)程 Worker worker = new Worker(queue, mailBox); worker.start(); } //4、提供一個(gè)方法, 讓調用者能夠把任務(wù)安排起來(lái) public void schedule(Runnable command, long time) { Task task = new Task(command, time); queue.put(task); synchronized (mailBox) { mailBox.notify(); } } }
3、測試代碼
其中添加了4個(gè)任務(wù), 分別是2s、5s、7s、10s后執行。
public static void main(String[] args) { Timer timer = new Timer(); timer.schedule(new Runnable() { @Override public void run() { System.out.println("郝夢(mèng)武一號任務(wù)執行, 執行代號:閃電; 定時(shí)時(shí)間:2s"); } }, 2000); timer.schedule(new Runnable() { @Override public void run() { System.out.println("郝夢(mèng)武二號任務(wù)執行, 執行代號:暴風(fēng); 定時(shí)時(shí)間:5s"); } }, 5000); timer.schedule(new Runnable() { @Override public void run() { System.out.println("郝夢(mèng)武三號任務(wù)執行, 執行代號:狂風(fēng); 定時(shí)時(shí)間:7s"); } }, 7000); timer.schedule(new Runnable() { @Override public void run() { System.out.println("郝夢(mèng)武三號任務(wù)執行, 執行代號:地震; 定時(shí)時(shí)間:10s"); } }, 10000); }
4、測試結果
以上就是本文的全部?jì)热?,希望對大家的學(xué)習有所幫助,也希望大家多多支持腳本之家。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。
Copyright ? 2009-2021 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 珠海市特網(wǎng)科技有限公司 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站