Java中定時器Timer致命缺點(附學習方法)


簡介


    這篇文章我一直在糾結到底要不要寫,不想寫一來由於定時器用法比較簡單,二來是面試中也不常問。後來仍是決定寫了主要是想把本身分析問題思路分享給你們,讓你們在學習過程當中可以參考,學習態度我相信大部分人沒有問題,特別是正在看我博文的小夥伴那更不用說了!!給大家點個狂力贊。接下來就是學習方法了,我發現近期來諮詢我問題的小夥伴學習姿式不對,因此我用Java中定時器Timer爲案例整理下個人學習方法。萬丈高樓平地起,因此我一向的作法都是先用最簡單,最簡單,最簡單案例先行!那就先來個Hello World吧!java

image.png





案例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類必需要實現裏面抽象方法
    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傳入的第二個參數是new Date(currenTime+2000)表示延遲2m執行task任務,這裏簡單使用方法就不過多的描述了,可是你們在學習過程當中遇到疑惑的問題必定要多嘗試多寫代碼測試,這是理解代碼必不可少的一部分,不要覺得能看懂就不寫了,像我在學習過程當中,若是稍微有疑問,我會立馬動手寫代碼測試,由於我知道有時候本身可能懂了,但那可能不是真正的懂,只有代碼能檢驗出來!下圖是程序控制臺打印結果。若是你們執行了你會發現一個問題,程序一直不結束運行,也就是程序不死。那是什麼致使這樣的結果呢?面試

image.png


線程不死問題?


    緣由分析:以下圖所示,主線程執行Timer timer = new Timer();會建立了一個新的子線程timer,timer線程經過死循環來取隊列裏面的任務task[1],隊列其實就是一個數組實現TaskQueue,隊列裏面若是沒有任務,那timer線程就會一直等待直到主線程調用schedule提交任務,主線程就會將task加入到TaskQueue隊列數組並通知timer線程執行任務並刪除隊列的第一個任務,若是是主線程提交的是定時任務,就會將任務從新加入隊列,任務執行完畢後,若是此時隊列爲空,timer線程就會繼續等待任務提交到隊列,一直會循環上面的過程。若是想退出timer線程,能夠調用cancel方法會退出死循環。線程不死緣由是timer線程一直在等待主線程提交任務,timer線程和主線程通訊是經過調用wait/notify實現。咱們以前作生產者/消費者案例時詳細介紹過,這裏老鐵再也不重複敘述了,能夠去翻看文章《母雞下蛋實例》。這個過程當中,咱們發現timer是一個單線程,我是單線程怎麼了?單線程也有錯嗎?思考個問題,若是timer這個單線程提交了兩個任務怎麼辦?咱們看下面代碼!spring

image.png





案例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:多任務執行測試,任務只能順序執行;
 * @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 {

        @Override
        public void run() {
            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執行時間過長,myTask2 被執行時間會被延遲;
        timer.schedule(myTask2,new Date(curTime+1000));
    }

}

    如上面程序代碼,timer線程提交了兩個任務myTask1,myTask2,myTask1任務會馬上執行,myTask2計劃延遲一秒執行,myTask1執行過程當中會休息10秒鐘,咱們觀察任務執行時間以下圖所示,myTask2任務是等待myTask1任務執行完畢後再執行的,其實myTask2只是延遲一秒執行,結果卻延遲了10秒,說明了timer單線程會串行化任務致使myTask2延遲執行,因此Timer是適合輕量級定時任務,若是設置大量任務,可能會存在延遲執行狀況數據庫

image.png



定時器實際應用場景


    在平常系統開發中,相信你遇到過相似須要重複執行的任務,好比天天凌晨2點清理數據庫某張表的垃圾數據,頁面顯示設備(服務器)運行狀態也須要每隔3秒調用設備狀態接口查詢設備狀況等,這些功能開發都須要用到定時器,固然Timer定時器也有自身的缺陷,好比它是單線程的,後面會說到線程池中的定時器是多線程的,能夠優化Timer,因此掌握Timer定時器爲後面學習高階內容打好基礎。知其然知其因此然,在實際應用中咱們能駕輕就熟!編程



學習方法心得


    你們能夠看到我最近幾篇文章分析多線程花了很多精力都在談論可見性,原子性,母雞下蛋生成消費問題等問題,由於這些特性是理解多線程的基礎,在我看來基礎又特別重要,因此怎麼反覆寫我認爲都不過度,在這以前有不少新手或者有2到3年工做經驗的童鞋常常會問我關於Java的學習方法,還有一大批童鞋一上來就要作springboot,ssm項目,我是不建議這麼幹的,你在作項目以前先要了解下servlet,mvc思想啊,這是基礎。我給他們的建議就是要紮實基礎,別上來就學高級的知識點或者框架,好比ReentrantLock源碼,線程池框架,就像你玩遊戲,一開始你就玩難度級別比較高的,一旦坡度比較高你就會比較難受吃力更別說對着書本了,這就是真正的從入門到放棄的過程。同時在學習的時候別光思考,以爲這個知識點本身會了就過了,這是不夠的須要多寫代碼,多實踐,你在這個過程當中再去加深本身對知識的理解與記憶,其實有不少知識你看起來是理解了,可是你沒有動手去實踐,也沒有真正理解,這樣只看不作的方法我是不推薦的,本人本科畢業後工做7年,一直從事Java一線的研發工做,擔任Java高級研發工程師,中間也帶過團隊,由於本身曾經踏着坑過來的,對學習程序仍是有必定的心得體會,我會在從此的日子裏持續整理把一些經驗和知識方面的經歷分享給你們,但願你們喜歡關注我。我是叫練,叫個口號就開始練!數組

總結下來就是兩句話:多動手,紮實基礎,從簡單作起,而後慢慢深刻!springboot



總結


    咱們用代碼簡述timer定時器提交任務,並說明了timer是單線程的適合輕量級的定時任務,這是它的缺陷。鑑於篇幅有限其中timer還有不少方法咱們沒有用代碼貼出來,好比定時執行,延遲執行,timer取消方法,但願你們一一對着書本執行起來,給你們推薦本多線程入門學習書籍,《Java多線程編程核心技術》,書本以案例爲主,也沒有特別難理解的案例,很是適合新手學習。若是須要pfd版請聯繫我!喜歡的請點贊加關注哦。我是叫練【公衆號】,邊叫邊練。服務器


image.png

相關文章
相關標籤/搜索