TimerTask

article/2025/8/30 9:53:04

详解java定时任务

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

一、简介

在java中一个完整定时任务需要由Timer、TimerTask两个类来配合完成。 API中是这样定义他们的,Timer:一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。由TimerTask:Timer 安排为一次执行或重复执行的任务。我们可以这样理解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指定时间执行线程任务...

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.................

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

对于上面三个实例,LZ只是简单的演示了一下,同时也没有讲解scheduleAtFixedRate方法的例子,其实该方法与schedule方法一样!

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的实际时间而发生改变,也就是scheduledExecutionTime(n+1)=realExecutionTime(n)+periodTime。也就是说如果第n个task由于某种情况导致这次的执行时间过程,最后导致systemCurrentTime>= scheduledExecutionTime(n+1),这是第n+1个task并不会因为到时了而执行,他会等待第n个task执行完之后再执行,那么这样势必会导致n+2个的执行实现scheduledExecutionTime放生改变即scheduledExecutionTime(n+2) = realExecutionTime(n+1)+periodTime。所以这两个方法更加注重保存间隔时间的稳定。

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更加侧重于保持执行频率的稳定。

三、Timer的缺陷

3.1、Timer的缺陷

Timer计时器可以定时(指定时间执行任务)、延迟(延迟5秒执行任务)、周期性地执行任务(每隔个1秒执行任务),但是,Timer存在一些缺陷。首先Timer对调度的支持是基于绝对时间的,而不是相对时间,所以它对系统时间的改变非常敏感。其次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);    //线程休眠3000                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }, 1000);    }        public void timerTwo(){        timer.schedule(new TimerTask() {            public void run() {                System.out.println("timerOne 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:1001timerOne invoked ,the time:3001

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

timerOne invoked ,the time:1000timerOne invoked ,the time:5000

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任务终止。

Exception in thread "Timer-0" java.lang.RuntimeException    at com.chenssy.timer.TimerTest04$1.run(TimerTest04.java:25)    at java.util.TimerThread.mainLoop(Timer.java:555)    at java.util.TimerThread.run(Timer.java:505)

对于Timer的缺陷,我们可以考虑 ScheduledThreadPoolExecutor 来替代。Timer是基于绝对时间的,对系统时间比较敏感,而ScheduledThreadPoolExecutor 则是基于相对时间;Timer是内部是单一线程,而ScheduledThreadPoolExecutor内部是个线程池,所以可以支持多个任务并发执行。

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:1003timerTwo,the time:2005

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://blog.csdn.net/lmj623565791/article/details/27109467


http://chatgpt.dhexx.cn/article/RNs4TkHT.shtml

相关文章

Java Timer TimerTask示例

Java java.util.Timer是一个实用程序类&#xff0c;可用于调度将来某个时间执行的线程。Java Timer类可用于计划要一次运行的任务或定期运行的任务。 Java TimerTask java.util.TimerTask是一个实现Runnable接口的抽象类&#xff0c;我们需要扩展这个类来创建我们自己的Timer…

Timer+TimerTask实现数字时钟

成果展示 布局 布局主题背景颜色采用#000000&#xff08;纯黑色&#xff09;&#xff0c;各TextView字体颜色采用#FFFFFF(纯白色)。 使用五个TextView&#xff0c;分别实现小时&#xff1a;分钟&#xff0c;秒钟&#xff0c;am&#xff0c;pm&#xff0c;周日&#xff0c;具体…

Timer + TimerTask 实现数字时钟

任务&#xff1a; 自己完成的&#xff1a; 遇到的问题&#xff1a; 1.Android Studio 中 calendar设置系统时区无效的问题 14&#xff1a;50它显示为06&#xff1a;50&#xff0c;时间显示错误&#xff0c;这是因为时区不同。 在8.0以后设置时区&#xff0c;有所改动&#xf…

java定时器-Timer和TimerTask详解

1、例子入手 package pers.growing.test;import java.util.Timer; import java.util.TimerTask;public class Main {/*** 延迟100ms后&#xff0c;间隔1s打印出&#xff1a;hello world** param args* throws InterruptedException*/public static void main(String[] args) t…

定时器Timer与TimerTask的使用

一&#xff1a;简介 在JDK类库中Timer主要负责计划任务的功能&#xff0c;也就是在指定时间执行某一任务&#xff0c;执行时候会在主线程之外起一个单独的线程执行指定的任务。该类主要是设置任务计划&#xff0c;但封装的类是TimerTask类。 TimerTask是一个实现了Runnable接口…

Java的定时器Timer和定时任务TimerTask应用以及原理简析

记录&#xff1a;272 场景&#xff1a;Java JDK自带的定时器Timer和定时任务TimerTask应用以及原理简析。在JDK工具包&#xff1a;java.util中可以找到源码&#xff0c;即java.util.Timer和java.util.TimerTask。TimerTask实现Runnable接口的run方法。Timer的属性TimerThread …

原码、补码、反码的转换

1.原码 &#xff08;1&#xff09;一个正数&#xff0c;他的原码就是它对应的二进制数字。 &#xff08;2&#xff09;一个负数&#xff0c;按照绝对值大小转换成的二进制数&#xff0c;然后最高位补1&#xff0c;就是负数的原码。 2.反码 &#xff08;1&#xff09;正数的反…

原码、补码、反码的关系

一. 机器数和真值 在学习原码, 反码和补码之前, 需要先了解机器数和真值的概念. 1、机器数 一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的&#xff0c;在计算机用一个数的最高位存放符号, 正数为0, 负数为1. 比如&#xff0c;十进制中的数 3 &…

原码,反码,补码,阶码,移码

本文转载自本站大佬“不去上课”&#xff0c;原文链接https://blog.csdn.net/ruidianbaihuo/article/details/87875178 原码&#xff0c;反码&#xff0c;补码&#xff0c;阶码&#xff0c;移码是什么?有什么区别(讨论机器数的表示) 本文内容参考自王达老师的《深入理解计算机…

原码、反码、补码之间的转换和简单运算

一、正整数的原码、反码、补码完全一样&#xff0c;即符号位固定为0&#xff0c;数值位相同 二、负整数的符号位固定为1&#xff0c;由原码变为补码时&#xff0c;规则如下&#xff1a; 1、原码符号位1不变&#xff0c;整数的每一位二进制数位求反&#xff0c;得到反码 2、反码…

原码,反码,补码的概念

计算机里都是以补码的形式存储数据&#xff0c;电脑只能识别二进制的0和1&#xff0c; 一个字节&#xff08;8位&#xff09;为例 原码 &#xff1a;最高位符号位&#xff0c;0代表正数&#xff0c;1代表负数&#xff0c;非符号位为该数字绝对值的二进制。 反码&#xff1a;正…

C语言——原码, 反码, 补码 详解

目录 一. 机器数和真值 1、机器数 2、真值 二. 原码, 反码, 补码的基础概念和计算方法. 1. 原码 2. 反码 3. 补码 三. 为何要使用原码, 反码和补码 四 原码, 反码, 补码 再深入 同余的概念 负数取模 开始证明 一. 机器数和真值 在学习原码, 反码和补码之前, 需要先…

原码、反码、补码、移码的概念及转换

目录 前言 1.原码 2.反码 3.补码 4.移码 前言 学习完数的小数点表示&#xff0c;下一个需要解决的问题就是数的机器码表示问题。 在计算机中对数据进行运算操作时&#xff0c;符号位应该如何表示&#xff1f;是否也同数值位一道参加运算操作呢&#xff1f;如果参加&…

mt7620芯片处理器核心资料

MT7620产品系统整合了2T2R 802.11n Wi-Fi 收发器、580MHz MIPS 24KEc™ 中央处理器 (CPU)、5 端口高速以太网络端口物理层 (Ethernet PHY)、HNAT、存储器加速器、USB2.0 主机/设备&#xff0c;以及多种慢速输入输出 (U客论坛)。MT7620A 支持 PCIe、RGMII&#xff0c;适用于 AC7…

mt7620参考设计原理图,mt7620芯片资料,mt7620处理器资料

mt7620参考设计原理图,芯片资料,处理器资料 核心资料 芯片处理器资料 设计注意事项 处理器大全 音频语音设计资料文档(U客论坛) MT7620产品系统整合了2T2R 802.11n Wi-Fi 收发器、580MHz MIPS 24KEc™ 中央处理器 (CPU)、5 端口高速以太网络端口物理层 (Ethernet PHY)、HNAT…

MT7682参考手册,MT7682芯片资料介绍

MT7682 Reference Manual 2.总线体系结构与内存映射 MediaTek MT7682采用32位多AHB矩阵&#xff0c;为物联网和可穿戴设备提供低功耗、快速、灵活的数据操作.表2.1-1显示了总线主机之间的互连(Cortex-M4&#xff0c;四个spi主站&#xff0c;spi从机&#xff0c;调试系统&#…

MT7628 openwrt学习(1)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、拿到板子之后干的事情二、openwrt编译文件下载tftp中的坑 最后是SSH和简单的编译 前言 主要是用来记录我的MT7628踩坑经历&#xff0c;所有东西都是开源的…

mt7621芯片更换ram

最近公司设备的内存占用率过高&#xff0c;经常性的导致设备挂掉&#xff0c;于是准备换一个ram。 解决方案&#xff1a; 在openwrt目录下&#xff0c;进入target/linux/ramips/dts目录&#xff0c;找到设备使用的CPU型号对应的dts文件&#xff0c;然后修改其中内容。将memory的…

MT7621处理器资料解析,MT7621数据表

MT7621处理器资料解析,MT7621数据表 MT7621 Wi-Fi 系统单芯片包含功能强大的 880 MHz MIPS 1004KEc™ 双核心中心处理器 (CPU)、5 端口 Gigabit 以太网络交换器,以及RGMII、PCIe、USB、SD-XC 等众多连接选项。这款全新系统单芯片亦随附我们经现场验证的硬件支持,涵盖网络地…

MT6261处理器参数介绍,MT6261芯片资料

MT6261处理器&#xff1a; MT6261是一种基于低功耗CMOS工艺的集成前沿电源管理单元、模拟基带和无线电电路的单片芯片。 MT6261是一种功能丰富、功能非常强大的用于高端GSM/GPRS能力的单芯片解决方案。基于32位ARM7EJ-S TM RISC处理器&#xff0c;MT6261的卓越处理能力TH高带…