简介


  这篇文章我一直在纠结到底要不要写,不想写一来因为定时器用法比较简单,二来是面试中也不常问。后来还是决定写了主要是想把自己分析问题思路分享给大家,让大家在学习过程中能够参考,学习态度我相信大部分人没有问题,特别是正在看我博文的小伙伴那更不用说了!!给你们点个狂力赞。接下来就是学习方法了,我发现近期来咨询我问题的小伙伴学习姿势不对,所以我用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. Python正则表达式re.findall("[A-Za-z]([A-Za-z0-9])*[.]txt",'Abc2019.txt')的结果为什么是['9']

    在<Python妙用re.sub分析正则表达式匹配过程>中老猿分析了findall函数的返回情况,老猿前一阵子在执行这个语句时: >>> re.findall(" ...

  2. PyQt(Python+Qt)学习随笔:Designer中属性设置界面的属性字体使用粗黑体的含义

    老猿Python博文目录 老猿Python博客地址 使用了好几个月的Designer,今天才发现属性编辑界面的属性名有的为粗而黑,有的则不是,如图: 稍微测试了一下,发现是对属性值进行过调整,不再是缺 ...

  3. C++编程指南(6-7)

    六.函数设计 函数是C++/C程序的基本功能单元,其重要性不言而喻.函数设计的细微缺点很容易导致该函数被错用,所以光使函数的功能正确是不够的.本章重点论述函数的接口设计和内部实现的一些规则. 函数接口 ...

  4. 蒲公英 · JELLY技术周刊 Vol.33: 前端基础课堂开课啦~

    蒲公英 · JELLY技术周刊 Vol.33 页面文件太大?图片过大了吧:页面加载白屏?很有可能是字体文件还没加载完:页面加载时间过长?多半是主进程被阻塞--该怎么办呢?快来小葵,咳咳,「蒲公英」前端 ...

  5. 第 7 篇 Scrum 冲刺博客

    每天举行会议 会议照片: 昨天已完成的工作与今天计划完成的工作及工作中遇到的困难: 成员姓名 昨天完成工作 今天计划完成的工作 工作中遇到的困难 蔡双浩 补充注释,初步查找bug 修改bug 无 陈创 ...

  6. 学习笔记:Splay

    代码适中.非常灵活的平衡树. 需要前置:二叉搜索树. 一些基础的函数: int idx, ch[N][2], cnt[N], sz[N], fa[N]; /* idx 是节点计数, ch[i][0 / ...

  7. 7、Spring Cloud Hystrix

    1.Spring Cloud Hystrix简介 (1).分布式问题 复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败. 多个微服务之间调用的时候,假设微服务A调 ...

  8. js--数组的map()方法的使用

    javaScript中Array.map()的用法 前言 作为一个刚刚踏入前端世界的小白,工作中看到身边同事大佬写的代码就像古诗一样简介整齐,而我的代码如同一堆散沙,看上去毫无段落感,而且简单的功能需 ...

  9. django APIview使用

    1.APIview使用 ModelVIewSet 是对 APIView 封装 ModelSerializer 是对 Serializer 1.1 在 user/urls.py 中添加路由 urlpat ...

  10. Linux下keepalived配置

    1.背景 节点1:192.168.12.35 节点2:192.168.12.36 2.keepalived安装 使用yum仓库安装keepalived [root@node01 ~]# yum ins ...