Java的定时器Timer和定时任务TimerTask应用以及原理简析
記錄:272
場景:Java JDK自帶的定時器Timer和定時任務TimerTask應用以及原理簡析。在JDK工具包:java.util中可以找到源碼,即java.util.Timer和java.util.TimerTask。TimerTask實現Runnable接口的run方法。Timer的屬性TimerThread thread繼承Thread。因此,Timer天生就具備多線程屬性。這個輕量級的定時器和定時任務,在多線程的場景中使用極其便利和靈活。
版本:
JDK 1.8 Spring Boot 2.6.3 Spring Framework 5.3.151.基礎
Timer和TimerTask成對出現,Timer是定時器,TimerTask是定時任務。換句話說,定時任務TimerTask是給定時器Timer執行的具體任務。在JDK 1.8中TimerTask是抽象類(abstract),使用者繼承TimerTask,并實現抽象方法run()方法即可。TimerTask實現Runnable接口的run()。
1.1 TimerTask
TimerTask,即java.util.TimerTask,一個抽象類(abstract修飾的類),實現(implements)可被線程執行的Runnable接口。
TimerTask需關注的內容。
(1)屬性:final Object lock,多線程同步時使用。在Timer的TimerThread中同步鎖synchronized會使用。
(2)屬性:int state,標識TimerTask的當前狀態。包括VIRGIN,SCHEDULED,EXECUTED,CANCELLED。
(3)屬性:long nextExecutionTime,下一次執行的時間。
(4)屬性:long period,重復任務的執行頻率。
(5)方法:boolean cancel(),取消任務,實際標記任務狀態state為CANCELLED。
(6)方法:scheduledExecutionTime(),獲取調度時間。
(7)方法:protected TimerTask(),無參構造方法。
(8)方法:abstract void run(),線程執行具體函數,使用者的具體業務任務就在這個方法中寫。
1.2 TaskQueue
TaskQueue,即java.util.TaskQueue。TaskQueue維護一個屬性TimerTask[] queue,這是Timer的任務線程操作的底層隊列。在Timer中有TaskQueue屬性,是給任務線程TimerThread使用的。
TaskQueue需關注的內容。
(1)屬性:TimerTask[] queue,隊列中存放TimerTask。Timer的TimerThread通過遍歷queue,取出TimerTask,根據TimerTask屬性來確定當前是否執行。。
注意:使用者不需要直接操作TimerTask[] queue,它是交給Timer的TimerThread操作。
1.3 TimerThread
TimerThread,即java.util.TimerThread。繼承Thread類。因此,天生就具備多線程屬性。
TimerThread需關注的內容。
(1)屬性:TaskQueue queue,TimerThread操作queue來找到當前需要執行的定時任務TimerTask。根據執行策略執行定時任務TimerTask中的run方法。
(2)方法:void run(),是TimerThread從Thread中繼承的方法。線程執行的具體內容,必須在這個方法中寫入或者在這個方法中被調用。TimerThread實現細節方法是void mainLoop()。在run方法中會調用mainLoop(),這樣定時任務就被任務線程調用到。
(3)方法:void mainLoop(),TimerThread中邏輯細節落地方法。本方法內容在while (true)中,只要隊列有任務就會無限循環。方法核心內容就是遍歷TimerThread的屬性:TaskQueue queue,找到符合條件的需執行的TimerTask,并執行TimerTask的run方法。
(4)方法: void start(),此方法是TimerThread從Thread繼承來的,作用就是啟動線程。TimerThread的start()方在Timer創建時就會調用。Timer創建后,TimerThread就啟動了。
1.4 Timer
Timer,即java.util.Timer。使用者操定時器就是操作這個類的任務調度方法。
Timer的需關注的內容。
(1)屬性:final TaskQueue queue,Timer存放所有的定時任務的隊列,定時器Timer的核心就是遍歷這個隊列,找到當前時間下,符合條件的需執行的定時任務TimerTask。
(2)屬性:final TimerThread thread,Timer執行定時任務的后臺線程。定時任務具體執行邏輯在TimerThread的run方法中。TimerThread 線程在Timer的構造函數中會調用TimerThread的start()啟動線程。
(3)構造函數
1>Timer(),無參構造函數。
2>Timer(boolean isDaemon),有參構造函數。設置定時器線程是否為守護線程。就是設置屬性:TimerThread thread,調用Thread的setDaemon()方法。
3>Timer(String name),有參構造函數。設置線程名稱。
4>Timer(String name, boolean isDaemon)。有參構造函數。設置線程名稱和設置是否為守護線程。
(4)任務調度函數
1>任務調度函數
操作Timer定時器,實際就是操作如下任務調度函數。
public void schedule(TimerTask task, long delay); public void schedule(TimerTask task, Date time); public void schedule(TimerTask task, long delay, long period); public void schedule(TimerTask task, Date firstTime, long period); public void scheduleAtFixedRate(TimerTask task, long delay, long period); public void scheduleAtFixedRate(TimerTask task, Date firstTime,long period);2>任務調度函數(sched)
任務調度函數都會調用一個私有方法sched。
private void sched(TimerTask task, long time, long period);TimerTask?task:繼承抽象類TimerTask,重寫abstract void run()方法,把具體業務任務寫在這個方法里。
long time:調度任務的起始時間值。調度是從這個時間為計算起點。
long period:任務調度頻率。
把定時任務TimerTask添加到隊列,就是在這個方法中實現。
(5)方法:void cancel(),取消隊列里所有任務。
(6)方法:void purge(),清除隊列里所有任務。
2.Timer原理解析
Timer原理解析,來自JDK中java.util.Timer源碼邏輯。如有疑惑,可直接翻閱源碼,便一目了然。
2.1 添加定時任務
把TimerTask添加到Timer內部的TaskQueue中。
2.2.1 自定義實現TimerTask的 run()
自定義實現抽象類TimerTask的 run()方法,即任務需要執行的業務邏輯寫在run方法中。
2.2.2 創建定時器Timer
創建定時器Timer,會做三件核心事情。
(1)創建任務隊列:TaskQueue queue。
(2)創建定時器線程:TimerThread thread。
(3)啟動定時器線程:執行TimerThread的start()方法,即后臺循環掃描的定時器線程啟動。
2.2.3 調用Timer任務調度函數
添加定時任務就在調用Timer任務調度方法中完成。
(1)設置定時器任務調度參數。
(2)把自定義任務傳遞給任務調度函數。
2.2.4 Timer任務調度函數核心邏輯
Timer暴露給使用者的所有任務調度函數,最終都會落地在一個私有方法,即
void sched(TimerTask task, long time, long period);
核心邏輯如下:
(1)同步鎖:synchronized(queue),鎖住隊列。隊列所有操作在此鎖內完成。
(2)同步鎖:synchronized(task.lock),鎖住任務。
此鎖內主要操作:設置TimerTask任務的執行時間,執行頻率,任務狀態。操作完成立即解鎖。
(3)隊列TaskQueue queue,把設置好的TimerTask添加到TaskQueue 中。
(4)解除同步鎖:synchronized(queue)。
2.2 執行定時任務
執行定時任務,在Timer的定時器線程TimerThread thread中,即線程的run()方法中,最終落地方式是 mainLoop()。注意mainLoop()是在run()方法中調用,是邏輯集中寫在mainLoop()中,增加代碼易讀性。
2.2.1 while (true)入口
mainLoop()入口是while (true),即使循環掃描。
2.2.2 同步鎖:synchronized(queue)
同步鎖:synchronized(queue),鎖住隊列,一個Timer共用一個隊列,因此使用synchronized有效。
2.2.3 判斷隊列是否為空
使用 while (queue.isEmpty() && newTasksMayBeScheduled)判斷隊列是否為空,如果為空,則 queue.wait()等待,注意wait()是java.lang.Object的方法,因此,此時while在卡主狀態,直到queue.notify()被調用,才會繼續。
2.2.4 確認隊列為空跳出循環
使用if (queue.isEmpty()),判斷隊列確定為空了,那么就break跳出循環,其實任務線程就優雅結束了。
2.2.5 取出一個任務TimerTask
取出一個任務:task = queue.getMin();
2.2.6 同步鎖:synchronized(task.lock),操作任務
使用同步鎖:synchronized(task.lock),鎖住任務。判斷任務是否可執行。
(1)判斷任務狀態。
(2)取當前系統時間:System.currentTimeMillis()。
(3)取出的任務執行時間: task.nextExecutionTime。
(4)判斷當前系統時間和任務執行時間,來確定任務是否需要執行。
(5)使用任務狀態標識taskFired=true任務需啟動;否則,不啟動。。
(6)任務操作完成,解除同步鎖:synchronized(task.lock)。
2.2.7 解除同步鎖:synchronized(queue)
2.2.8 執行任務:task.run()。
自定義的定時任務TimerTask,在此處就被執行。
3.案例
本例每隔60秒,獲取一次UUID。
(1)實現TimerTask的run方法,把具體執行的業務任務寫到run方法中。
(2)創建Timer對象,配置定時任務和傳入TimerTask對象。
Timer提供多種任務調度策略,本例使用固定頻率任務調度。
TimeMonitor,實現了InitializingBean接口,在afterPropertiesSet中初始化定時器。也就是TimeMonitor被容器初始化完成后,就會觸發Timer定時器初始化,Timer定時器內部的任務線程會啟動和任務隊列會把使用者的定時任務添加到隊列。Timer定時器就會生效運行。
@Slf4j @Service public class TimeMonitor implements InitializingBean {@Overridepublic void afterPropertiesSet() throws Exception {startTimerMonitor();}private void startTimerMonitor() {TimerTask timerTask = new TimerTask() {@Overridepublic void run() {log.info("定時任務執行開始.");String uuid = UUID.randomUUID().toString().replace("-", "").toUpperCase();log.info("執行業務,獲取序列號,UUID = " + uuid);log.info("定時任務執行完成.");}};// 定時器Timer timer = new Timer();// 延時時間long delayTime = 6000L;// 執行頻率long period = 1000 * 60;timer.scheduleAtFixedRate(timerTask, delayTime, period);} }4.測試
根據輸出日志查看定時調度情況。
?以上,感謝。
2022年6月11日
總結
以上是生活随笔為你收集整理的Java的定时器Timer和定时任务TimerTask应用以及原理简析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java制作JDK8文档搜索引擎项目并部
- 下一篇: c语言亲戚计算器