Java中定時器Timer致命缺點案例詳解
簡介
這篇文章我一直在糾結到底要不要寫,不想寫一來因為定時器用法比較簡單,二來是面試中也不常問。后來還是決定寫了主要是想把自己分析問題思路分享給大家,讓大家在學習過程中能夠參考,學習態(tài)度我相信大部分人沒有問題,特別是正在看我博文的小伙伴那更不用說了?。〗o你們點個狂力贊。接下來就是學習方法了,我發(fā)現(xiàn)近期來咨詢我問題的小伙伴學習姿勢不對,所以我用Java中定時器Timer為案例整理下我的學習方法。萬丈高樓平地起,所以我一貫的做法都是先用最簡單,最簡單,最簡單案例先行!那就先來個Hello World吧!
案例1:定時器打印Hello World!
import java.text.ParseException; import java.util.Date; import java.util.Timer; import java.util.TimerTask; /** * @author :jiaolian * @date :Created in 2021-01-05 20:42 * @description:Timer啟動后內置線程不銷毀 * @modified By: */ public class TimerThreadNoStopTest { //TimerTask為抽象類,繼承TimerTask類必須要實現(xiàn)里面抽象方法 private static class Task extends TimerTask { @Override public void run() { System.out.println("hello world!"); } } public static void main(String[] args) throws ParseException { Timer timer = new Timer(); Task task = new Task(); long currenTime = System.currentTimeMillis(); //提交Task線程;程序按傳入日期運行 timer.schedule(task,new Date(currenTime)); }
如上面程序代碼,Timer提交了一個task任務并傳入了currenTime當前時間,控制臺馬上打印了"hello world!",如果schedule傳入的第二個參數(shù)是new Date(currenTime+2000)表示延遲2m執(zhí)行task任務,這里簡單使用方法就不過多的描述了,但是大家在學習過程中遇到疑惑的問題一定要多嘗試多寫代碼測試,這是理解代碼必不可少的一部分,不要以為能看懂就不寫了,像我在學習過程中,如果稍微有疑問,我會立馬動手寫代碼測試,因為我知道有時候自己可能懂了,但那可能不是真正的懂,只有代碼能檢驗出來!下圖是程序控制臺打印結果。如果大家執(zhí)行了你會發(fā)現(xiàn)一個問題,程序一直不結束運行,也就是程序不死。那是什么導致這樣的結果呢?
線程不死問題?
原因分析:如下圖所示,主線程執(zhí)行Timer timer = new Timer();會創(chuàng)建了一個新的子線程timer,timer線程通過死循環(huán)來取隊列里面的任務task[1],隊列其實就是一個數(shù)組實現(xiàn)TaskQueue,隊列里面如果沒有任務,那timer線程就會一直等待直到主線程調用schedule提交任務,主線程就會將task加入到TaskQueue隊列數(shù)組并通知timer線程執(zhí)行任務并刪除隊列的第一個任務,如果是主線程提交的是定時任務,就會將任務重新加入隊列,任務執(zhí)行完畢后,如果此時隊列為空,timer線程就會繼續(xù)等待任務提交到隊列,一直會循環(huán)上面的過程。如果想退出timer線程,可以調用cancel方法會退出死循環(huán)。線程不死原因是timer線程一直在等待主線程提交任務,timer線程和主線程通信是通過調用wait/notify實現(xiàn)。我們之前做生產者/消費者案例時詳細介紹過,這里老鐵不再重復敘述了,可以去翻看文章《母雞下蛋實例》。這個過程中,我們發(fā)現(xiàn)timer是一個單線程,我是單線程怎么了?單線程也有錯嗎?思考個問題,如果timer這個單線程提交了兩個任務怎么辦?我們看下面代碼!
案例2:單線程問題
import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; /** * @author :jiaolian * @date :Created in 2021-01-06 10:53 * @description:多任務執(zhí)行測試,任務只能順序執(zhí)行; * @modified By: */ public class MultTaskExecuteTest { private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private static class MyTask1 extends TimerTask { @Override public void run() { System.out.println("task1 begin:"+SIMPLE_DATE_FORMAT.format(new Date())); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("task1 end:"+SIMPLE_DATE_FORMAT.format(new Date())); } } private static class MyTask2 extends TimerTask { System.out.println("task2 begin:"+SIMPLE_DATE_FORMAT.format(new Date())); System.out.println("task2 end:"+SIMPLE_DATE_FORMAT.format(new Date())); public static void main(String[] args){ Timer timer = new Timer(); MyTask1 myTask1 = new MyTask1(); MyTask2 myTask2 = new MyTask2(); long curTime = System.currentTimeMillis(); System.out.println("當前時間:"+SIMPLE_DATE_FORMAT.format(curTime)); timer.schedule(myTask1,new Date(curTime)); //myTask1執(zhí)行時間過長,myTask2 被執(zhí)行時間會被延遲; timer.schedule(myTask2,new Date(curTime+1000)); }
如上面程序代碼,timer線程提交了兩個任務myTask1,myTask2,myTask1任務會立刻執(zhí)行,myTask2計劃延遲一秒執(zhí)行,myTask1執(zhí)行過程中會休息10秒鐘,我們觀察任務執(zhí)行時間如下圖所示,myTask2任務是等待myTask1任務執(zhí)行完畢后再執(zhí)行的,其實myTask2只是延遲一秒執(zhí)行,結果卻延遲了10秒,說明了timer單線程會串行化任務導致myTask2延遲執(zhí)行,所以Timer是適合輕量級定時任務,如果設置大量任務,可能會存在延遲執(zhí)行情況。
定時器實際應用場景
在日常系統(tǒng)開發(fā)中,相信你遇到過類似需要重復執(zhí)行的任務,比如每天凌晨2點清理數(shù)據(jù)庫某張表的垃圾數(shù)據(jù),頁面顯示設備(服務器)運行狀態(tài)也需要每隔3秒調用設備狀態(tài)接口查詢設備情況等,這些功能開發(fā)都需要用到定時器,當然Timer定時器也有自身的缺陷,比如它是單線程的,后面會說到線程池中的定時器是多線程的,可以優(yōu)化Timer,所以掌握Timer定時器為后面學習高階內容打好基礎。知其然知其所以然,在實際應用中我們能得心應手!
學習方法心得
大家可以看到我最近幾篇文章分析多線程花了不少精力都在談論可見性,原子性,母雞下蛋生成消費問題等問題,因為這些特性是理解多線程的基礎,在我看來基礎又特別重要,所以怎么反復寫我認為都不過分,在這之前有很多新手或者有2到3年工作經驗的童鞋經常會問我關于Java的學習方法,還有一大批童鞋一上來就要做springboot,ssm項目,我是不建議這么干的,你在做項目之前先要了解下servlet,mvc思想啊,這是基礎。我給他們的建議就是要扎實基礎,別上來就學高級的知識點或者框架,比如ReentrantLock源碼,線程池框架,就像你玩游戲,一開始你就玩難度級別比較高的,一旦坡度比較高你就會比較難受吃力更別說對著書本了,這就是真正的從入門到放棄的過程。同時在學習的時候別光思考,覺得這個知識點自己會了就過了,這是不夠的需要多寫代碼,多實踐,你在這個過程中再去加深自己對知識的理解與記憶,其實有很多知識你看起來是理解了,但是你沒有動手去實踐,你也沒有真正理解,這樣只看不做的方法我是不推薦的,本人本科畢業(yè)后工作7年,一直從事Java一線的研發(fā)工作,擔任Java高級研發(fā)工程師,中間也帶過團隊,因為自己曾經踏著坑過來的,對學習程序還是有一定的心得體會,我會在今后的日子里持續(xù)整理把一些經驗和知識方面的經歷分享給大家,希望大家喜歡關注我。我是叫練,叫個口號就開始練!
總結下來就是兩句話:多動手,扎實基礎,從簡單做起,然后慢慢深入!
總結
我們用代碼簡述timer定時器提交任務,并說明了timer是單線程的適合輕量級的定時任務,這是它的缺陷。
到此這篇關于Java中定時器Timer致命缺點(附學習方法)的文章就介紹到這了,更多相關Java定時器Timer內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
java實現(xiàn)emqx設備上下線監(jiān)聽詳解
這篇文章主要為大家介紹了java實現(xiàn)emqx設備上下線監(jiān)聽詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-07-07