Java中定时器Timer致命缺点(附学习方法)

简介


这篇文章我一直在纠结到底要不要写,不想写一来由于定时器用法比较简单,二来是面试中也不常问。后来仍是决定写了主要是想把本身分析问题思路分享给你们,让你们在学习过程当中可以参考,学习态度我相信大部分人没有问题,特别是正在看我博文的小伙伴那更不用说了!!给大家点个狂力赞。接下来就是学习方法了,我发现近期来咨询我问题的小伙伴学习姿式不对,因此我用Java中定时器Timer为案例整理下个人学习方法。万丈高楼平地起,因此我一向的作法都是先用最简单,最简单,最简单案例先行!那就先来个Hello World吧!
image.pngjava

案例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这个单线程提交了两个任务怎么办?咱们看下面代码!
image.pngspring

案例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高级研发工程师,中间也带过团队,由于本身曾经踏着坑过来的,对学习程序仍是有必定的心得体会,我会在从此的日子里持续整理把一些经验和知识方面的经历分享给你们,但愿你们喜欢关注我。我是叫练,叫个口号就开始练!
总结下来就是两句话:多动手,扎实基础,从简单作起,而后慢慢深刻!数组

总结


咱们用代码简述timer定时器提交任务,并说明了timer是单线程的适合轻量级的定时任务,这是它的缺陷。鉴于篇幅有限其中timer还有不少方法咱们没有用代码贴出来,好比定时执行,延迟执行,timer取消方法,但愿你们一一对着书本执行起来,给你们推荐本多线程入门学习书籍,《Java多线程编程核心技术》,书本以案例为主,也没有特别难理解的案例,很是适合新手学习。若是须要pfd版请联系我!喜欢的请点赞加关注哦。我是叫练【公众号】,边叫边练。
image.pngspringboot