Java定時器Timer與TimerTask的使用詳解
一:簡介
在JDK類庫中Timer主要負責(zé)計劃任務(wù)的功能,也就是在指定時間執(zhí)行某一任務(wù),執(zhí)行時候會在主線程之外起一個單獨的線程執(zhí)行指定的任務(wù)。該類主要是設(shè)置任務(wù)計劃,但封裝的類是TimerTask類。 TimerTask是一個實現(xiàn)了Runnable接口的抽象類,代表一個可被執(zhí)行的任務(wù),執(zhí)行任務(wù)的代碼要放在其子類中(TimerTask是抽象類)。
二:Timer整體類圖
TimerTask類:主要為定時任務(wù)的具體內(nèi)容。
Timer類中含有3個類:Timer、TimerThread、TaskQueue。
Timer類主要是設(shè)置定時任務(wù),配置用戶期望的任務(wù)執(zhí)行時間、執(zhí)行次數(shù)、執(zhí)行內(nèi)容
TimerThread類為Thread的擴展類,會一直從TaskQueue中獲取下標為1的TimerTask進行執(zhí)行。并根據(jù)該TimerTask是否需要重復(fù)執(zhí)行來決定是否放回到TaskQueue中。
TaskQueue中存放一些列將要執(zhí)行的TimerTask,以數(shù)組的形式存放,下標約?。ㄗⅲ合聵藶?不處理,即使用的最小下標為1),則表明優(yōu)先級越高。
public class Timer { //默認給線程命名:"Timer-" + serialNumber() public Timer() { this("Timer-" + serialNumber()); } //指定Timer的名字 public Timer(String name) { thread.setName(name); thread.start(); } //isDaemon:是否為守護線程 public Timer(boolean isDaemon) { this("Timer-" + serialNumber(), isDaemon); } //給定線程名字,守護線程與否 public Timer(String name, boolean isDaemon) { thread.setName(name); //setDaemon是指定線程是否為守護線程,true是守護線程,當(dāng)主線程退出, 守護線程退出 thread.setDaemon(isDaemon); thread.start(); } } //TimerThread類 繼承Thread class TimerThread extends Thread{ private void mainLoop() { ...... //每次執(zhí)行下標為1的TimerTask task = queue.getMin(); ...... } } //TaskQueue 類 class TaskQueue { //TaskQueue 中存儲TimerTask private TimerTask[] queue = new TimerTask[128]; //返回數(shù)組中下標1的TimerTask TimerTask getMin() { return queue[1]; } }
三:Timer常用方法
1.Timer 類void schedule(TimerTask task, Date time)方法
1.task是定時任務(wù),time是task任務(wù)執(zhí)行時間.如果time時間早于當(dāng)前則立即執(zhí)行,否則在time時間執(zhí)行。
2.task只執(zhí)行一次,且執(zhí)行完Timer線程任務(wù)不結(jié)束,因為Timer不是守護線程,Timer timer = new Timer(true);
是指定Timer 為守護線程,當(dāng)task執(zhí)行結(jié)束后,Timer 線程會立即結(jié)束。
3.Timer 可以執(zhí)行多個定時任務(wù),TimerTask 將會按照隊列的方式一個一個被執(zhí)行,如果前面的任務(wù)耗時長,后面的任務(wù)執(zhí)行時間將會相應(yīng)的延后。
public class TimerDemo { //指定Timer為守護線程,run方法結(jié)束Timer線程會結(jié)束。 private static Timer timer = new Timer(true); //TimerTask 具體的定時任務(wù) static public class MyTask extends TimerTask { @Override public void run() { System.out.println("已運行,時間為:" + new Date()); } } public static void main(String[] args) { //創(chuàng)建定時任務(wù) MyTask myTask = new MyTask(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //早于當(dāng)前時間,定時任務(wù)會立即執(zhí)行 String dateString = "2020-06-01 09:56:00"; try { Date taskDate = sdf.parse(dateString); //啟動定時任務(wù) timer.schedule(myTask,taskDate); } catch (Exception e) { e.printStackTrace(); } } }
結(jié)果:
已運行,時間為:Wed Jul 01 14:48:06 CST 2020
2.Timer 類void schedule(TimerTask task, Date firstTime, long period)方法
TimerTask任務(wù)在指定 firstTime任務(wù)之后,周期性每隔period時間,無限期循環(huán)的執(zhí)行某一任務(wù)。
3.TimerTask 類boolean cancel()方法
TimerTask 類boolean cancel()方法,是將自身任務(wù)從任務(wù)隊列中移除。
public class TimerDemo { private static Timer timer = new Timer(); static public class MyTaskA extends TimerTask { @Override public void run() { //只輸出一次,MyTaskA 任務(wù)將會移除 System.out.println("MyTaskA已運行,時間為:" + new Date()); //執(zhí)行TimerTask的cancel方法,將自身任務(wù)從隊列中移除 this.cancel(); } } static public class MyTaskB extends TimerTask { @Override public void run() { //移除MyTaskA任務(wù),MyTaskB 任務(wù)不受影響 System.out.println("MyTaskB已運行,時間為:" + new Date()); } } public static void main(String[] args) { MyTaskA myTaskA = new MyTaskA(); MyTaskB myTaskB = new MyTaskB(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateString = "2020-07-01 15:19:00"; try { Date taskDate = sdf.parse(dateString); timer.schedule(myTaskA,taskDate,2000); timer.schedule(myTaskB,taskDate,2000); } catch (Exception e) { e.printStackTrace(); } } }
結(jié)果:
MyTaskA已運行,時間為:Wed Jul 01 15:24:47 CST 2020
MyTaskB已運行,時間為:Wed Jul 01 15:24:47 CST 2020
MyTaskB已運行,時間為:Wed Jul 01 15:24:49 CST 2020
MyTaskB已運行,時間為:Wed Jul 01 15:24:51 CST 2020
MyTaskB已運行,時間為:Wed Jul 01 15:24:53 CST 2020
MyTaskB已運行,時間為:Wed Jul 01 15:24:55 CST 2020
MyTaskB已運行,時間為:Wed Jul 01 15:24:57 CST 2020
MyTaskB已運行,時間為:Wed Jul 01 15:24:59 CST 2020
4.Timer 類void cancel()方法
Timer 類void cancel()方法與TimerTask 類boolean cancel()方法將自身任務(wù)從任務(wù)隊列中移除不同,它是將所有的任務(wù)都從隊列中移除。
public class TimerDemo { private static Timer timer = new Timer(); static public class MyTaskA extends TimerTask { @Override public void run() { System.out.println("MyTaskA已運行,時間為:" + new Date()); //執(zhí)行Timer類的cancel方法,將Timer中所有任務(wù)從隊列中移除 timer.cancel(); } } static public class MyTaskB extends TimerTask { @Override public void run() { //MyTaskB也會被移除,因為MyTaskA與MyTaskB是隊列執(zhí)行,先執(zhí)行MyTaskA再執(zhí)行MyTaskB //MyTaskA中走了timer.cancel(),所以MyTaskB不會執(zhí)行 System.out.println("MyTaskB已運行,時間為:" + new Date()); } } public static void main(String[] args) { MyTaskA myTaskA = new MyTaskA(); MyTaskB myTaskB = new MyTaskB(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateString = "2020-07-01 15:19:00"; try { Date taskDate = sdf.parse(dateString); timer.schedule(myTaskA,taskDate,2000); timer.schedule(myTaskB,taskDate,2000); } catch (Exception e) { e.printStackTrace(); } } }
結(jié)果
MyTaskA已運行,時間為:Wed Jul 01 16:20:48 CST 2020
源碼:
public void cancel() { synchronized(queue) { thread.newTasksMayBeScheduled = false; queue.clear(); queue.notify(); // In case queue was already empty. } }
執(zhí)行cancel需要搶占queue鎖,如果搶不到,則TimerTask 任務(wù)會繼續(xù)執(zhí)行。
5.Timer 類 void schedule(TimerTask task, long delay)方法
該方法以schedule(TimerTask task, long delay)方法的執(zhí)行時間為基準,在此時間上延遲指定的毫秒數(shù)delay后執(zhí)行一次TimerTask 任務(wù)。
6.Timer 類 void schedule(TimerTask task, long delay, long period)方法
該方法以schedule(TimerTask task, long delay, long period)方法的執(zhí)行時間為基準,在此時間上延遲指定的毫秒數(shù)delay后,執(zhí)行TimerTask 任務(wù),之后間隔period毫秒無限循環(huán)執(zhí)行TimerTask 任務(wù)。
7.Timer 類void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)方法
TimerTask任務(wù)在指定 firstTime任務(wù)之后,周期性每隔period時間,無限期循環(huán)的執(zhí)行某一任務(wù)。
四:重點
scheduleAtFixedRate(TimerTask task, Date firstTime, long period)與 schedule(TimerTask task, Date firstTime, long period)方法區(qū)別
1.相同點:兩個方法都是按照順序執(zhí)行,無線程安全問題
2.任務(wù)不延時相同點:如果任務(wù)執(zhí)行時間小于任務(wù)時間間隔,則第二次任務(wù)的開始時間是任務(wù)開始時間+period
3.任務(wù)延時相同點:如果任務(wù)執(zhí)行時間大于任務(wù)時間間隔,導(dǎo)致第二次任務(wù)開始時候,上一個任務(wù)還未結(jié)束,則第二次任務(wù)的開始時間是在第一次任務(wù)結(jié)束時候立即執(zhí)行。
scheduleAtFixedRate方法與schedule方法的追趕性問題
scheduleAtFixedRate方法具有追趕性?。。。∪绻媱潏?zhí)行時間早于當(dāng)前時間,執(zhí)行scheduleAtFixedRate方法后立即執(zhí)行定時任務(wù),根據(jù)任務(wù)間隔時間與當(dāng)前時間減去計劃執(zhí)行時間,補上之前少執(zhí)行的任務(wù)。 schedule方法不具有追趕性。!?。。?/p>
public class TimerDemo { private static Timer timer = new Timer(); static public class MyTaskA extends TimerTask { @Override public void run() { System.out.println("MyTaskA已運行,時間為:" + new Date()); try { //任務(wù)執(zhí)行時間是1s Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("MyTaskA已結(jié)束,時間為:" + new Date()); } } public static void main(String[] args) { MyTaskA myTaskA = new MyTaskA(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String dateString = "2020-07-02 14:31:00"; try { Date taskDate = sdf.parse(dateString); //3s執(zhí)行一次任務(wù) timer.scheduleAtFixedRate(myTaskA,taskDate,3000); } catch (Exception e) { e.printStackTrace(); } } }
結(jié)果:
MyTaskA已運行,時間為:Thu Jul 02 14:34:38 CST 2020
MyTaskA已結(jié)束,時間為:Thu Jul 02 14:34:39 CST 2020
MyTaskA已運行,時間為:Thu Jul 02 14:34:39 CST 2020
MyTaskA已結(jié)束,時間為:Thu Jul 02 14:34:40 CST 2020
MyTaskA已運行,時間為:Thu Jul 02 14:34:40 CST 2020
MyTaskA已結(jié)束,時間為:Thu Jul 02 14:34:41 CST 2020
MyTaskA已運行,時間為:Thu Jul 02 14:34:41 CST 2020
MyTaskA已結(jié)束,時間為:Thu Jul 02 14:34:42 CST 2020
MyTaskA已運行,時間為:Thu Jul 02 14:34:42 CST 2020
MyTaskA已結(jié)束,時間為:Thu Jul 02 14:34:43 CST 2020
MyTaskA已運行,時間為:Thu Jul 02 14:34:43 CST 2020
MyTaskA已結(jié)束,時間為:Thu Jul 02 14:34:44 CST 2020
MyTaskA已運行,時間為:Thu Jul 02 14:34:44 CST 2020
MyTaskA已結(jié)束,時間為:Thu Jul 02 14:34:45 CST 2020
MyTaskA已運行,時間為:Thu Jul 02 14:34:45 CST 2020
MyTaskA已結(jié)束,時間為:Thu Jul 02 14:34:46 CST 2020
MyTaskA已運行,時間為:Thu Jul 02 14:34:46 CST 2020
由上述執(zhí)行結(jié)果可知,在Thu Jul 02 14:34:38 CST 2020啟動定時任務(wù)后,沒有間隔3s再執(zhí)行下一次任務(wù),而是任務(wù)結(jié)束就立即執(zhí)行,這是因為任務(wù)計劃執(zhí)行時間早于當(dāng)前時間,需要將這期間沒有執(zhí)行的任務(wù)補上。
五: 總結(jié)
java中可以使用定時任務(wù)的功能,針對不同的定時任務(wù)可以根據(jù)不同的API進行處理,從本質(zhì)來說改技術(shù)仍然屬于多線程,基于多線程實現(xiàn),不管用再多的框架實現(xiàn)定時器功能,請不要忘了這事java基礎(chǔ),框架實現(xiàn)的基礎(chǔ)。
到此這篇關(guān)于Java定時器Timer與TimerTask的使用詳解的文章就介紹到這了,更多相關(guān)Java定時器Timer與TimerTask內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot 集成 Jasypt 對數(shù)據(jù)庫加密以及踩坑的記錄分享
這篇文章主要介紹了SpringBoot 集成 Jasypt 對數(shù)據(jù)庫加密以及踩坑,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08intellij idea如何配置網(wǎng)絡(luò)代理
intellij idea所在的這臺電腦本身上不了網(wǎng),要通過代理上網(wǎng),那么intellij idea怎么設(shè)置代理上網(wǎng)呢?今天通過本文給大家分享intellij idea如何配置網(wǎng)絡(luò)代理,感興趣的朋友一起看看吧2023-10-10java利用StringTokenizer分割字符串的實現(xiàn)
利用java.util.StringTokenizer的方法,可以將一個字符串拆分為一系列的標記,本文就來介紹一下java利用StringTokenizer分割字符串的實現(xiàn),感興趣的可以了解一下2023-10-10