一、简介
      在java的jdk中提供了Timer、TimerTask两个类来做定时任务。

Timer是一种定时器工具,用来在一个后台线程计划执行指定任务,而TimerTask一个抽象类,它的子类代表一个可以被Timer计划的任务。
---------------------

Timer类
      在工具类Timer中,提供了四个构造方法,每个构造方法都启动了计时器线程,同时Timer类可以保证多个线程可以共享单个Timer对象而无需进行外部同步,所以Timer类是线程安全的。但是由于每一个Timer对象对应的是单个后台线程,用于顺序执行所有的计时器任务,一般情况下我们的线程任务执行所消耗的时间应该非常短,但是由于特殊情况导致某个定时器任务执行的时间太长,那么他就会“独占”计时器的任务执行线程,其后的所有线程都必须等待它执行完,这就会延迟后续任务的执行,使这些任务堆积在一起,具体情况我们后面分析。

当程序初始化完成Timer后,定时任务就会按照我们设定的时间去执行,Timer提供了schedule方法,该方法有多种重载方式来适应不同的情况,如下:

schedule(TimerTask task, Date time):安排在指定的时间执行指定的任务。

schedule(TimerTask task, Date firstTime, long period) :安排指定的任务在指定的时间开始进行重复的固定延迟执行。

schedule(TimerTask task, long delay) :安排在指定延迟后执行指定的任务。

schedule(TimerTask task, long delay, long period) :安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。

同时也重载了scheduleAtFixedRate方法,scheduleAtFixedRate方法与schedule相同,只不过他们的侧重点不同,区别后面分析。

scheduleAtFixedRate(TimerTask task, Date firstTime, long period):安排指定的任务在指定的时间开始进行重复的固定速率执行。

scheduleAtFixedRate(TimerTask task, long delay, long period):安排指定的任务在指定的延迟后开始进行重复的固定速率执行。

TimerTask
      TimerTask类是一个抽象类,由Timer 安排为一次执行或重复执行的任务。它有一个抽象方法run()方法,该方法用于执行相应计时器任务要执行的操作。因此每一个具体的任务类都必须继承TimerTask,然后重写run()方法。

另外它还有两个非抽象的方法:

boolean cancel():取消此计时器任务。

long scheduledExecutionTime():返回此任务最近实际执行的安排执行时间。

二、实例

2.1、指定延迟时间执行定时任务

public class TimerTest01 {
Timer timer;
public TimerTest01(int time){
timer = new Timer();
timer.schedule(new TimerTaskTest01(), time * 1000);
} public static void main(String[] args) {
System.out.println("timer begin....");
new TimerTest01(3);
}
} public class TimerTaskTest01 extends TimerTask{ public void run() {
System.out.println("Time's up!!!!");
}
}

运行结果:

首先打印:timer begin....

3秒后打印:Time's up!!!!

2.2、在指定时间执行定时任务

public class TimerTest02 {
Timer timer; public TimerTest02(){
Date time = getTime();
System.out.println("指定时间time=" + time);
timer = new Timer();
timer.schedule(new TimerTaskTest02(), time);
} public Date getTime(){
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 11);
calendar.set(Calendar.MINUTE, 39);
calendar.set(Calendar.SECOND, 00);
Date time = calendar.getTime(); return time;
} public static void main(String[] args) {
new TimerTest02();
}
} public class TimerTaskTest02 extends TimerTask{ @Override
public void run() {
System.out.println("指定时间执行线程任务...");
}
}

当时间到达11:39:00时就会执行该线程任务,当然大于该时间也会执行!!执行结果为:

指定时间time=Tue Jun 10 11:39:00 CST 2014
指定时间执行线程任务...

如果大于该时间不想要执行的解决办法:

public class SandTimer {
public void timerRun() {
// 一天的毫秒数
long daySpan = 24 * 60 * 60 * 1000;
// 规定的每天时间09:16:00运行
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd 09:16:00");
// 首次运行时间
try {
Date startTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(sdf.format(new Date()));
// 如果今天的已经过了 首次运行时间就改为明天
if (System.currentTimeMillis() > startTime.getTime()){
startTime = new Date(startTime.getTime() + daySpan);
}
Timer t = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.print("定时器执行");
}
};
// 以每24小时执行一次
t.schedule(task, startTime, daySpan);
} catch (Exception e) {
e.printStackTrace();
}
} public static void main(String[] args) {
new SandTimer().timerRun();
}
---------------------
作者:小贪1
来源:CSDN
原文:https://blog.csdn.net/x2570799116/article/details/78860448
版权声明:本文为博主原创文章,转载请附上博文链接!

2.3、在延迟指定时间后以指定的间隔时间循环执行定时任务

public class TimerTest03 {
Timer timer; public TimerTest03(){
timer = new Timer();
timer.schedule(new TimerTaskTest03(), 1000, 2000);
} public static void main(String[] args) {
new TimerTest03();
}
} public class TimerTaskTest03 extends TimerTask{ @Override
public void run() {
Date date = new Date(this.scheduledExecutionTime());
System.out.println("本次执行该线程的时间为:" + date);
}
}

运行结果:

本次执行该线程的时间为:Tue Jun 10 21:19:47 CST 2014
本次执行该线程的时间为:Tue Jun 10 21:19:49 CST 2014
本次执行该线程的时间为:Tue Jun 10 21:19:51 CST 2014
本次执行该线程的时间为:Tue Jun 10 21:19:53 CST 2014
本次执行该线程的时间为:Tue Jun 10 21:19:55 CST 2014
本次执行该线程的时间为:Tue Jun 10 21:19:57 CST 2014
................

对于这个线程任务,如果我们不将该任务停止,他会一直运行下去。

2.4、分析schedule和scheduleAtFixedRate

1、schedule(TimerTask task, Date time)、schedule(TimerTask task, long delay)

对于这两个方法而言,如果指定的计划执行时间scheduledExecutionTime<= systemCurrentTime,则task会被立即执行。scheduledExecutionTime不会因为某一个task的过度执行而改变。

2、schedule(TimerTask task, Date firstTime, long period)、schedule(TimerTask task, long delay, long period)

这两个方法与上面两个就有点儿不同的,前面提过Timer的计时器任务会因为前一个任务执行时间较长而延时。在这两个方法中,每一次执行的task的计划时间会随着前一个task的实际时间而发生改变,比如说本来是1,3,5这样的时间间隔执行的,因为某种原因第一个任务执行了4秒钟,那么就变成了1,7,9

3、scheduleAtFixedRate(TimerTask task, Date firstTime, long period)、scheduleAtFixedRate(TimerTask task, long delay, long period)

在前面也提过scheduleAtFixedRate与schedule方法的侧重点不同,schedule方法侧重保存间隔时间的稳定,而scheduleAtFixedRate方法更加侧重于保持执行频率的稳定。为什么这么说,原因如下。在schedule方法中会因为前一个任务的延迟而导致其后面的定时任务延时,而scheduleAtFixedRate方法则不会,如果第n个task执行时间过长导致systemCurrentTime>= scheduledExecutionTime(n+1),则不会做任何等待他会立即执行第n+1个task,所以scheduleAtFixedRate方法执行时间的计算方法不同于schedule,而是scheduledExecutionTime(n)=firstExecuteTime +n*periodTime,该计算方法永远保持不变。所以scheduleAtFixedRate更加侧重于保持执行频率的稳定。

就是说,本来它是计划1,3,5这样的间隔执行的,那么一定会是1,3,5这样的时间点执行,不管前面的任务执行了多少时间。

三、Timer的缺陷

3.1、Timer的缺陷
     1.Timer对调度的支持是基于绝对时间的,而不是相对时间,所以它对系统时间的改变非常敏感。

2.Timer线程是不会捕获异常的,如果TimerTask抛出的了未检查异常则会导致Timer线程终止,同时Timer也不会重新恢复线程的执行,他会错误的认为整个Timer线程都会取消。           同时,已经被安排但尚未执行的TimerTask也不会再执行了,新的任务也不能被调度。故如果TimerTask抛出未检查的异常,Timer将会产生无法预料的行为。

1、Timer管理时间延迟缺陷

前面Timer在执行定时任务时只会创建一个线程任务,如果存在多个线程,若其中某个线程因为某种原因而导致线程任务执行时间过长,超过了两个任务的间隔时间,会发生一些缺陷:

public class TimerTest04 {
private Timer timer;
public long start; public TimerTest04(){
this.timer = new Timer();
start = System.currentTimeMillis();
} public void timerOne(){
timer.schedule(new TimerTask() {
public void run() {
System.out.println("timerOne invoked ,the time:" + (System.currentTimeMillis() - start));
try {
Thread.sleep(4000); //线程休眠4000
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 1000);
} public void timerTwo(){
timer.schedule(new TimerTask() {
public void run() {
System.out.println("timerTwo invoked ,the time:" + (System.currentTimeMillis() - start));
}
}, 3000);
} public static void main(String[] args) throws Exception {
TimerTest04 test = new TimerTest04(); test.timerOne();
test.timerTwo();
}
}

按照我们正常思路,timerTwo应该是在3s后执行,其结果应该是:

timerOne invoked ,the time:1001
timerOne invoked ,the time:3001

但是事与愿违,timerOne由于sleep(4000),休眠了4S,同时Timer内部是一个线程,导致timeOne所需的时间超过了间隔时间,结果:

timerOne invoked ,the time:1000
timerTwo invoked ,the time:5001

2、Timer抛出异常缺陷

如果TimerTask抛出RuntimeException,Timer会终止所有任务的运行。如下:

public class TimerTest04 {
private Timer timer; public TimerTest04(){
this.timer = new Timer();
} public void timerOne(){
timer.schedule(new TimerTask() {
public void run() {
throw new RuntimeException(); //抛出异常
}
}, 1000);
} public void timerTwo(){
timer.schedule(new TimerTask() { public void run() {
System.out.println("我会不会执行呢??");
}
}, 1000);
} public static void main(String[] args) {
TimerTest04 test = new TimerTest04();
test.timerOne();
test.timerTwo();
}
}

运行结果:timerOne抛出异常,导致timerTwo任务终止。

3.2、用ScheduledExecutorService替代Timer

1、解决问题一:

public class ScheduledExecutorTest {
private ScheduledExecutorService scheduExec; public long start; ScheduledExecutorTest(){
this.scheduExec = Executors.newScheduledThreadPool(2);
this.start = System.currentTimeMillis();
} public void timerOne(){
scheduExec.schedule(new Runnable() {
public void run() {
System.out.println("timerOne,the time:" + (System.currentTimeMillis() - start));
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},1000,TimeUnit.MILLISECONDS);
} public void timerTwo(){
scheduExec.schedule(new Runnable() {
public void run() {
System.out.println("timerTwo,the time:" + (System.currentTimeMillis() - start));
}
},2000,TimeUnit.MILLISECONDS);
} public static void main(String[] args) {
ScheduledExecutorTest test = new ScheduledExecutorTest();
test.timerOne();
test.timerTwo();
}
}

运行结果:

timerOne,the time:1003
timerTwo,the time:2005          这2个时间每次执行都会有些偏差

2、解决问题二

public class ScheduledExecutorTest {
private ScheduledExecutorService scheduExec; public long start; ScheduledExecutorTest(){
this.scheduExec = Executors.newScheduledThreadPool(2);
this.start = System.currentTimeMillis();
} public void timerOne(){
scheduExec.schedule(new Runnable() {
public void run() {
throw new RuntimeException();
}
},1000,TimeUnit.MILLISECONDS);
} public void timerTwo(){
scheduExec.scheduleAtFixedRate(new Runnable() {
public void run() {
System.out.println("timerTwo invoked .....");
}
},2000,500,TimeUnit.MILLISECONDS);
} public static void main(String[] args) {
ScheduledExecutorTest test = new ScheduledExecutorTest();
test.timerOne();
test.timerTwo();
}
}

运行结果:

timerTwo invoked .....
timerTwo invoked .....
timerTwo invoked .....
timerTwo invoked .....
timerTwo invoked .....
timerTwo invoked .....
timerTwo invoked .....
timerTwo invoked .....
timerTwo invoked .....
........................

原文出自:http://cmsblogs.com/?p=1175。尊重作者的成果,转载请注明出处!    此文我做了一点点的修改

详解java定时任务---Timer篇的更多相关文章

  1. 详解java定时任务

    在我们编程过程中如果需要执行一些简单的定时任务,无须做复杂的控制,我们可以考虑使用JDK中的Timer定时任务来实现.下面LZ就其原理.实例以及Timer缺陷三个方面来解析JavaTimer定时器. ...

  2. Protocol Buffer技术详解(Java实例)

    Protocol Buffer技术详解(Java实例) 该篇Blog和上一篇(C++实例)基本相同,只是面向于我们团队中的Java工程师,毕竟我们项目的前端部分是基于Android开发的,而且我们研发 ...

  3. Java多线程超级详解(只看这篇就够了)

    多线程能够提升程序性能,也属于高薪必能核心技术栈,本篇会全面详解Java多线程.@mikechen 主要包含如下几点: 基本概念 很多人都对其中的一些概念不够明确,如同步.并发等等,让我们先建立一个数 ...

  4. 超全详解Java开发环境搭建

    摘自:https://www.cnblogs.com/wangjiming/p/11278577.html 超全详解Java开发环境搭建   在项目产品开发中,开发环境搭建是软件开发的首要阶段,也是必 ...

  5. 「跬步千里」详解 Java 内存模型与原子性、可见性、有序性

    文题 "跬步千里" 主要是为了凸显这篇文章的基础性与重要性(狗头),并发编程这块的知识也确实主要围绕着 JMM 和三大性质来展开. 全文脉络如下: 1)为什么要学习并发编程? 2) ...

  6. 详解Java GC的工作原理+Minor GC、FullGC

    详解Java GC的工作原理+Minor GC.FullGC 引用地址:http://www.blogjava.net/ldwblog/archive/2013/07/24/401919.html J ...

  7. 【three.js详解之一】入门篇

    [three.js详解之一]入门篇   开场白 webGL可以让我们在canvas上实现3D效果.而three.js是一款webGL框架,由于其易用性被广泛应用.如果你要学习webGL,抛弃那些复杂的 ...

  8. 详解Java中的clone方法

    详解Java中的clone方法 参考:http://blog.csdn.net/zhangjg_blog/article/details/18369201/ 所谓的复制对象,首先要分配一个和源对象同样 ...

  9. java基础(十五)----- Java 最全异常详解 ——Java高级开发必须懂的

    本文将详解java中的异常和异常处理机制 异常简介 什么是异常? 程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常. Java异常的分类和类结构图 1.Java中的所 ...

随机推荐

  1. 使用spring框架,用xml方式进行bean装配出现“The fully qualified name of the bean's class, except if it serves...”

    使用spring框架,用xml方式进行bean装配出现“The fully qualified name of the bean's class, except if it serves...”. 原 ...

  2. 20175224 2018-2019-2 《Java程序设计》第八周学习总结

    教材学习内容总结 第十五章 泛型与集合框架 泛型 泛型:目的为建立具有类型安全的集合框架. 泛型类声明:class People<E> class 名称<泛型列表> 链表 定义 ...

  3. gprinter佳博打印机androidSDK

    最近在用佳博的SDK做打印的功能,由于一直做的是.net,没有android的基础,这个功能我做了一个多月,包括前期调研佳博打印机的打印方式.佳博打印机有两种打印方式,一种是标签打印,要用到TscCo ...

  4. Introduction to Unity UI

    https://www.raywenderlich.com/795-introduction-to-unity-ui-part-1 https://www.raywenderlich.com/794- ...

  5. Unity more efficient find

    Unity caches GameObjects based on their tags, so in a big scene with lots of objects, GameObject.Fin ...

  6. Mac上配置maven+eclipse+spark开发环境

    1.安装jdk 2.下载scala-ide.官网:http://scala-ide.org 3.安装maven 4.在eclipse中,配置maven的安装了路径.偏好设置--->maven-- ...

  7. 【转】Android-Input 按键字符映射文件&输入设备配置文件

    https://source.android.com/devices/input/key-character-map-files 按键字符映射文件 按键字符映射文件(.kcm 文件)负责将 Andro ...

  8. Jira7.10.1在Windows环境下的安装和配置

    jira安装的环境准备   1.   jira7.10的运行是依赖java环境的,也就是说需要安装jdk并且要是1.8以上版本: Java -version   2.  还需要为jira创建对应的数据 ...

  9. C16记技术服务支持

    1.首页笔记:可以通过点击加号添加笔记的分类,还可以查看最近的10条笔记 2.图形:可以通过查看笔记的比例来分析自己最近的状况 3.快速添加笔记:点击添加笔记能够直接进行笔记 4.时间轴:能够通过文字 ...

  10. linux小计

    一.查看远程主机开放端口命令 nc -zv 10.10.79.89 5151 二.远程登录ssh/scp yum install sshpass sshpass -p youpassword scp ...