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

到此這篇關于Java線程中的Timer和TimerTask原理詳解的文章就介紹到這了,更多相關Java的Timer和TimerTask內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
MyBatis通用Mapper和PageHelper的過程詳解
這篇文章主要介紹了MyBatis通用Mapper和PageHelper的相關知識,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11
解決@ConfigurationProperties注解的使用及亂碼問題
這篇文章主要介紹了解決@ConfigurationProperties注解的使用及亂碼問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10
Java編程發(fā)展歷史(動力節(jié)點Java學院整理)
Java的歷史可以追溯到1991年4月,Sun公司的James Gosling領導的綠色計劃(Green Project)開始著力發(fā)展一種分布式系統(tǒng)結構,使其能夠在各種消費性電子產(chǎn)品上運行,他們使用了C/C++/Oak語言。由于多種原因,綠色計劃逐漸陷于停滯狀態(tài)2017-03-03
SpringCloud Gateway自動裝配實現(xiàn)流程詳解
Spring Cloud Gateway旨在為微服務架構提供一種簡單有效的、統(tǒng)一的 API 路由管理方式。Spring Cloud Gateway 作為 Spring Cloud 生態(tài)系中的網(wǎng)關,它不僅提供統(tǒng)一的路由方式,并且基于 Filter 鏈的方式提供了網(wǎng)關基本的功能,例如:安全、監(jiān)控/埋點和限流等2022-10-10
Spring MVC 關于controller的字符編碼問題
在使用springMVC框架構建web應用,客戶端常會請求字符串、整型、json等格式的數(shù)據(jù),通常使用@ResponseBody注解使 controller回應相應的數(shù)據(jù)而不是去渲染某個頁面。2017-03-03
Spring Boot實現(xiàn)qq郵箱驗證碼注冊和登錄驗證功能
這篇文章主要給大家介紹了關于Spring Boot實現(xiàn)qq郵箱驗證碼注冊和登錄驗證功能的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-12-12
idea使用外置tomcat配置springboot詳細步驟
昨天小編遇到一個問題使用springboot自帶的tomcat啟動沒有任何問題,不知道idea使用外置tomcat配置springboot該如何設置的,今天小編給大家分享一篇教程幫助大家解決這個問題2021-07-07

