简介


  这篇文章我一直在纠结到底要不要写,不想写一来因为定时器用法比较简单,二来是面试中也不常问。后来还是决定写了主要是想把自己分析问题思路分享给大家,让大家在学习过程中能够参考,学习态度我相信大部分人没有问题,特别是正在看我博文的小伙伴那更不用说了!!给你们点个狂力赞。接下来就是学习方法了,我发现近期来咨询我问题的小伙伴学习姿势不对,所以我用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类必须要实现里面抽象方法
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任务,这里简单使用方法就不过多的描述了,但是大家在学习过程中遇到疑惑的问题一定要多尝试多写代码测试,这是理解代码必不可少的一部分,不要以为能看懂就不写了,像我在学习过程中,如果稍微有疑问,我会立马动手写代码测试,因为我知道有时候自己可能懂了,但那可能不是真正的懂,只有代码能检验出来!下图是程序控制台打印结果。如果大家执行了你会发现一个问题,程序一直不结束运行,也就是程序不死。那是什么导致这样的结果呢?

线程不死问题?


  原因分析:如下图所示,主线程执行Timer timer = new Timer();会创建了一个新的子线程timer,timer线程通过死循环来取队列里面的任务task[1],队列其实就是一个数组实现TaskQueue,队列里面如果没有任务,那timer线程就会一直等待直到主线程调用schedule提交任务,主线程就会将task加入到TaskQueue队列数组并通知timer线程执行任务并删除队列的第一个任务,如果是主线程提交的是定时任务,就会将任务重新加入队列,任务执行完毕后,如果此时队列为空,timer线程就会继续等待任务提交到队列,一直会循环上面的过程。如果想退出timer线程,可以调用cancel方法会退出死循环。线程不死原因是timer线程一直在等待主线程提交任务,timer线程和主线程通信是通过调用wait/notify实现。我们之前做生产者/消费者案例时详细介绍过,这里老铁不再重复叙述了,可以去翻看文章《母鸡下蛋实例》。这个过程中,我们发现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:多任务执行测试,任务只能顺序执行;
* @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是适合轻量级定时任务,如果设置大量任务,可能会存在延迟执行情况

定时器实际应用场景


  在日常系统开发中,相信你遇到过类似需要重复执行的任务,比如每天凌晨2点清理数据库某张表的垃圾数据,页面显示设备(服务器)运行状态也需要每隔3秒调用设备状态接口查询设备情况等,这些功能开发都需要用到定时器,当然Timer定时器也有自身的缺陷,比如它是单线程的,后面会说到线程池中的定时器是多线程的,可以优化Timer,所以掌握Timer定时器为后面学习高阶内容打好基础。知其然知其所以然,在实际应用中我们能得心应手!

学习方法心得


  大家可以看到我最近几篇文章分析多线程花了不少精力都在谈论可见性,原子性,母鸡下蛋生成消费问题等问题,因为这些特性是理解多线程的基础,在我看来基础又特别重要,所以怎么反复写我认为都不过分,在这之前有很多新手或者有2到3年工作经验的童鞋经常会问我关于Java的学习方法,还有一大批童鞋一上来就要做springboot,ssm项目,我是不建议这么干的,你在做项目之前先要了解下servlet,mvc思想啊,这是基础。我给他们的建议就是要扎实基础,别上来就学高级的知识点或者框架,比如ReentrantLock源码,线程池框架,就像你玩游戏,一开始你就玩难度级别比较高的,一旦坡度比较高你就会比较难受吃力更别说对着书本了,这就是真正的从入门到放弃的过程。同时在学习的时候别光思考,觉得这个知识点自己会了就过了,这是不够的需要多写代码,多实践,你在这个过程中再去加深自己对知识的理解与记忆,其实有很多知识你看起来是理解了,但是你没有动手去实践,也没有真正理解,这样只看不做的方法我是不推荐的,本人本科毕业后工作7年,一直从事Java一线的研发工作,担任Java高级研发工程师,中间也带过团队,因为自己曾经踏着坑过来的,对学习程序还是有一定的心得体会,我会在今后的日子里持续整理把一些经验和知识方面的经历分享给大家,希望大家喜欢关注我。我是叫练,叫个口号就开始练!

总结下来就是两句话:多动手,扎实基础,从简单做起,然后慢慢深入!

 

 

总结


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

Java中定时器Timer致命缺点(附学习方法)的更多相关文章

  1. Java进阶(十八)Java实现定时器(Timer)

    Java实现定时器(Timer) 绪 在应用开发中,经常需要一些周期性的操作,比如每5分钟执行某一操作等.对于这样的操作最方便.高效的实现方式就是使用java.util.Timer工具类.java.u ...

  2. java多线程--定时器Timer的使用

    定时的功能我们在手机上见得比较多,比如定时清理垃圾,闹钟,等等.定时功能在java中主要使用的就是Timer对象,他在内部使用的就是多线程的技术. Time类主要负责完成定时计划任务的功能,就是在指定 ...

  3. java中定时器总结

    java实现定时器的四种方式: 一. /** * 延迟20000毫秒执行 java.util.Timer.schedule(TimerTask task, long delay) */ public ...

  4. 2.1多线程(java学习笔记) java中多线程的实现(附静态代理模式)

    一.多线程 首先我们要清楚程序.进程.线程的关系. 首先进程从属于程序,线程从属于进程. 程序指计算机执行操作或任务的指令集合,是一个静态的概念. 但我们实际运行程序时,并发程序因为相互制约,具有“执 ...

  5. Java中定时器相关实现的介绍与对比之:Timer和TimerTask

    Timer和TimerTask JDK自带,具体的定时任务由TimerTask指定,定时任务的执行调度由Timer设定.Timer和TimerTask均在包java.util里实现. 本文基于java ...

  6. java中定时器的简单使用

    1.首先肯定是容器一启动就要启动定时器,所以我们可以选择把定时器写在一个监听器里,容器一启动所以监听器也就跟着启动,然后定时器就可以工作了. 第一步,把自己写的监听器加到web.xml中: 第二步,写 ...

  7. Java中使用Timer和TimerTask实现多线程

    转自:http://www.bdqn.cn/news/201305/9303.shtml 摘要:Timer是一种线程设施,用于安排以后在后台线程中执行的任务.可安排任务执行一次,或者定期重复执行,可以 ...

  8. Java中的Timer和TimerTask在Android中的用法(转)

    转自:http://blog.csdn.net/zuolongsnail/article/details/8168689 在开发中我们有时会有这样的需求,即在固定的每隔一段时间执行某一个任务.比如UI ...

  9. [ImportNew]Java中的Timer类和TimerTask类

    http://www.importnew.com/9978.html java.util.Timer是一个实用工具类,该类用来调度一个线程,使它可以在将来某一时刻执行. Java的Timer类可以调度 ...

随机推荐

  1. PyQt(Python+Qt)学习随笔:model/view架构中QTableView视图的数据无法显示问题

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 在使用QTableView来显示如下数据时: 在model中插入数据是使用如下类似代码: for c ...

  2. PyQt(Python+Qt)学习随笔:视图中类QAbstractItemView的dragDropOverwriteMode属性

    老猿Python博文目录 老猿Python博客地址 一.属性的作用 dragDropOverwriteMode属性用于控制视图的拖放行为,如果其值为True,则视图中选定的数据将在拖拽数据放下时被覆盖 ...

  3. JVM 垃圾回收?全面详细安排!

    写在前面: 小伙伴儿们,大家好!今天来学习Java虚拟机相关内容,作为面试必问的知识点,来深入了解一波! 思维导图: image-20201207153125210 1,判断对象是否死亡 我们在进行垃 ...

  4. JQuery获取父,子,兄弟节点

    jQuery.parent(expr) // 查找父节点,可以传入expr进行过滤,比如$("span").parent()或者$("span").parent ...

  5. 美国SEC主席离任,Panda Global 前瞻数字资产监管政策变化

    在上上个周末,也就是6月20号,美国的证券行业发生了一件值得反复回味的的事情--美国SEC现任主席Jay Clayton宣布即将离任,对于数字资产行业而言,未来监管政策将如何演变突然有了无限的遐想空间 ...

  6. Windows的API功能查询

    在逆向分析时,一些Windows的API函数往往是我们的突破口.但这些函数很难记得一清二楚,以下是我的查找办法,做个小结. 官网 https://docs.microsoft.com/en-us/wi ...

  7. 链判断运算符和Null 判断运算符

    链判断运算符 如果我们要获取一个对象的深层嵌套属性,例如获取文章标题res.data.article.title,然后为了安全起见,我们肯定不能直接这样获取,万一res对象没有article属性了呢, ...

  8. MySql字符集与排序规则详解

    前段时间往MySQL中存入emoji表情或生僻字.繁体字时,报错无法添加,研究后发现这是字符集编码的问题,今天就来分析一下各个字符集与排序规则 一.字符集 先说字符,字符是各种文字和符号的总称,包括各 ...

  9. ORACLE启用非默认监听端口

    1.修改listener.ora文件 LISTENER_TEST =         (DESCRIPTION =                 (ADDRESS = (PROTOCOL = TCP ...

  10. 2020-2021-1 20209307 《Linux内核原理与分析》第二周作业

    1.寻址方式和常用汇编指令 寄存器寻址:movl %eax,%edx  相当于edx=eax 立即寻址:movl $0x123,%edx   相当于edx=0x123 直接寻址:movl 0x123, ...