定时/延时任务-Timer用法

devtools/2024/11/25 10:00:41/

文章目录

  • 1. 概要
  • 2. 简述
    • 2.1 固定速率
    • 2.2 固定延时
    • 2.3 区别
  • 3. Timer 的用法
    • 3.1 固定延时 - public void schedule(TimerTask task, long delay, long period)
      • 3.1.1 解释
    • 3.2 固定延时 - public void schedule(TimerTask task, Date firstTime, long period)
    • 3.3 固定速率 - public void scheduleAtFixedRate(TimerTask task, long delay, long period)
      • 3.3.1 解释
    • 3.4 固定速率 - public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
    • 3.5 非周期任务 - public void schedule(TimerTask task, Date time)
    • 3.6 非周期任务 - schedule(TimerTask task, long delay)
  • 4. 小结

1. 概要

上一篇文章地址:定时/延时任务-自己实现一个简单的定时器
在上一篇文章中,我们自己实现了一个简单的 Timer 并提出了一些缺点,下面我们就来看看 JDK 中的 Timer 用法

2. 简述

在 Java Development Kit (JDK) 中,java.util.Timer 是一个用于调度任务的工具类。Timer 类使用一个后台线程来遍历队列中的任务,同时可以按照固定的延时或者固定的速率来重复执行。
首先在介绍 Timer 的 API 之前,先来看两个概念:

2.1 固定速率

固定速率 策略表示任务在固定的时间间隔内重复执行,不管任务的执行时间有多长,如果任务的执行时间超过了时间间隔,那么下一个任务会在当前任务执行完毕之后就会马上开始执行,下面是一个例子:假设我们设置了一个固定速率为 5 的任务,从 0s 开始执行,也就是说这个任务 5s 执行一次:

  • 第一次执行:0s 开始执行一次,假设执行时间是 3s
  • 第二次执行:5s 开始执行第二次,假设执行的时候被阻塞了,执行了 8s
  • 第三次执行:13s 开始执行第三次,假设执行的时候被阻塞了,执行了 3s
  • 第四次执行:16s 开始执行第四次,假设执行的时候没有被阻塞
  • 第四次执行:20s 开始执行第五次,假设执行的时候没有被阻塞

看了上面的过程分析,你可能有点懵,没关系,等到下面的时候会有例子并解释

2.2 固定延时

固定延时 策略表示任务在当前任务执行完成之后,固定延时一段时间再执行下一个任务,下面是一个例子:假设我们设置了一个固定速率为 5 的任务,从 0s 开始执行,也就是说这个任务 5s 执行一次:

  • 第一次执行:0s 开始执行一次,假设执行时间是 3s
  • 第二次执行:5s 开始执行第二次,假设执行的时候被阻塞了,执行了 8s
  • 第三次执行:13s 开始执行第三次,假设执行的时候被阻塞了,执行了 3s
  • 第四次执行:18s 开始执行第四次,假设执行的时候没有被阻塞
  • 第四次执行:23s 开始执行第五次,假设执行的时候没有被阻塞

2.3 区别

看了上面两种方式的分析,可以做一个小的总结:

  • 固定速率: 任务会按照固定时间间隔执行,如果任务执行的时间大于时间间隔,那么下一个任务会马上执行
  • 固定延时: 任务会按照固定延时执行,如果任务执行的时间小于时间间隔,那么两次任务的执行时间间隔就是设置的延时;如果任务执行的时间大于时间间隔,那么两次任务执行的时间间隔就是任务执行的时间,也就是说下一次任务会马上执行
  • 固定速率 这种方式比适合用于严格要求按照时间间隔执行的任务,比如心跳探测、数据收集等…
  • 固定延时 执行任务的时间不固定,但是得确保每一次任务执行完之后有一定时间间隔再执行下一次的任务,比如日志收集、数据清理等…

Timer__36">3. Timer 的用法

下面我们就来介绍下 Timer 的几个 API 的用法

TimerTask_task_long_delay_long_period_38">3.1 固定延时 - public void schedule(TimerTask task, long delay, long period)

这个 API 的意思是:延时 delay 后开始按照 period 的间隔执行

java">public class Pra {public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {try {System.out.println("Thread-Current: " + Thread.currentThread().getName() + ", time = " + getTime());if(Math.random() < 0.5){System.out.println("sleep: 3s");Thread.sleep(3000);} else {System.out.println("sleep: 8s");Thread.sleep(8000);}} catch (InterruptedException e) {e.printStackTrace();}}}, 0, 5000);}private static String getTime() {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");return sdf.format(new Date());}}

输出结果如下:
在这里插入图片描述

3.1.1 解释

解释:Timer 源码中对于任务的添加是线程被唤醒后获取到任务,之后就会立马计算出下一次该任务的调度时间加入队列中
有了上面的基础再来看输出,这就是为什么任务执行了 3s 最终还是在 49s 就开始执行下一个任务,因为 44s 执行任务的时候会根据当前执行时间算出下一个任务执行时间应该是 44 + 5 = 49s,而第二个任务 49s 执行的时候会算出下一个任务执行时间是 49 + 5 = 54s,但是由于任务执行时间长达 8s,导致下一个任务根本没时间被调度,所以只能在 57s 执行完之后去看队列,发现队列里面 54s 的任务早就到时间了,这时候算出下一个任务的执行时间是 57 + 5 = 02s,于是把这个任务加入到队列中,然后立马调度这个 54s 的任务,以此类推

你可能会有疑问:如果我任务执行时间是 8s,任务间隔是 3s,不会导致执行完一个任务之后队列中会有多个没有执行的任务吗?并不会,因为固定延时是按照当前时间来算下一个任务的计算时间,所以任务执行时间大于任务间隔时间的前提下,不管你间隔多少,都是以任务执行时间为主
但是对于固定速率又不一样了,这个我们下面会说

TimerTask_task_Date_firstTime_long_period_82">3.2 固定延时 - public void schedule(TimerTask task, Date firstTime, long period)

顾名思义,就是设置一个第一次启动的时间点,然后以 period 的延时执行,看下面的例子:

java">public class Pra {public static void main(String[] args) {Timer timer = new Timer();System.out.println("start time = " + getTime());timer.schedule(new TimerTask() {@Overridepublic void run() {try {System.out.println("Thread-Current: " + Thread.currentThread().getName() + ", time = " + getTime());if(Math.random() < 0.5){System.out.println("sleep: 3s");Thread.sleep(3000);} else {System.out.println("sleep: 8s");Thread.sleep(8000);}} catch (InterruptedException e) {e.printStackTrace();}}}, new Date(System.currentTimeMillis() + 10000), 5000);}private static String getTime() {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");return sdf.format(new Date());}}

在这里插入图片描述
上面 3.1 已经有解释了

TimerTask_task_long_delay_long_period_121">3.3 固定速率 - public void scheduleAtFixedRate(TimerTask task, long delay, long period)

当前延时 delay 开始,接着每次执行的间隔是 period

java">public class Pra {public static void main(String[] args) {Timer timer = new Timer();timer.scheduleAtFixedRate(new TimerTask() {@Overridepublic void run() {try {System.out.println("Thread-Current: " + Thread.currentThread().getName() + ", time = " + getTime());if(Math.random() < 0.5){System.out.println("sleep: 3s");Thread.sleep(3000);} else {System.out.println("sleep: 8s");Thread.sleep(8000);}} catch (InterruptedException e) {e.printStackTrace();}}}, 0, 5000);}private static String getTime() {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");return sdf.format(new Date());}}

在这里插入图片描述

3.3.1 解释

1.首先27s 开始执行第一次,然后往队列立马加入一个 32s 的下一次执行的任务,当前任务执行时间 3s
2. 第二次32s 开始执行第而次,然后往队列立马加入一个 37s 的下一次执行的任务,当前任务执行时间 8s
3. 第三次执行,当 工作线程 执行上一个任务之后已经到 40s 了,由于 40s 已经超过了 37s 的延时任务执行时间,于是会立马开始执行,这时候往队列里面添加一个 42s 执行的任务(下一次执行)
4. 第四次执行,当前任务执行时间 3s,执行完已经 43s 了,这时候执行结束会发现队列里面的 42s 的任务已经过期了,就会往队列立马添加一个 47s 的任务(下一次执行),然后立马开始执行,所以第四次执行时间是 43s
5. 第四次执行的时间是 3s ,执行完任务是 46s,这时候没到 47s,所以没有到下一次任务的执行时间,继续等待到 47s 执行 第五次任务

TimerTask_task_Date_firstTime_long_period_164">3.4 固定速率 - public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)

和上面一样就是选择某个首次执行时间点开始执行,后续速率 period

java">public class Pra {public static void main(String[] args) {Timer timer = new Timer();System.out.println("start time = " + getTime());timer.scheduleAtFixedRate(new TimerTask() {@Overridepublic void run() {try {System.out.println("Thread-Current: " + Thread.currentThread().getName() + ", time = " + getTime());if(Math.random() < 0.5){System.out.println("sleep: 3s");Thread.sleep(3000);} else {System.out.println("sleep: 8s");Thread.sleep(8000);}} catch (InterruptedException e) {e.printStackTrace();}}}, new Date(), 5000);}private static String getTime() {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");return sdf.format(new Date());}}

在这里插入图片描述

TimerTask_task_Date_time_201">3.5 非周期任务 - public void schedule(TimerTask task, Date time)

上面都是周期任务,下面这两个就是非周期任务,顾名思义就是只执行一次的任务,来看例子:

java">public class Pra {public static void main(String[] args) {Timer timer = new Timer();System.out.println("start time = " + getTime());timer.schedule(new TimerTask() {@Overridepublic void run() {try {System.out.println("Thread-Current: " + Thread.currentThread().getName() + ", time = " + getTime());if(Math.random() < 0.5){System.out.println("sleep: 3s");Thread.sleep(3000);} else {System.out.println("sleep: 8s");Thread.sleep(8000);}} catch (InterruptedException e) {e.printStackTrace();}}}, new Date(System.currentTimeMillis() + 10000));}private static String getTime() {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");return sdf.format(new Date());}}

在这里插入图片描述

TimerTask_task_long_delay_238">3.6 非周期任务 - schedule(TimerTask task, long delay)

延迟 delay 时间之后开始执行

java">public class Pra {public static void main(String[] args) {Timer timer = new Timer();System.out.println("start time = " + getTime());timer.schedule(new TimerTask() {@Overridepublic void run() {try {System.out.println("Thread-Current: " + Thread.currentThread().getName() + ", time = " + getTime());if(Math.random() < 0.5){System.out.println("sleep: 3s");Thread.sleep(3000);} else {System.out.println("sleep: 8s");Thread.sleep(8000);}} catch (InterruptedException e) {e.printStackTrace();}}}, 10000);}private static String getTime() {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");return sdf.format(new Date());}}

在这里插入图片描述

4. 小结

这篇文章中我们详细介绍了 Timer 的用法,下一篇文章就来安排上源码的详细解析





如有错误,欢迎指出!!!


http://www.ppmy.cn/devtools/136809.html

相关文章

mfc100u.dll是什么?分享几种mfc100u.dll丢失的解决方法

mfc100u.dll 是一个动态链接库&#xff08;DLL&#xff09;文件&#xff0c;属于 Microsoft Foundation Classes (MFC) 库的一部分。MFC 是微软公司开发的一套用于快速开发 Windows 应用程序的 C 类库。mfc100u.dll 文件包含了 MFC 库中一些常用的函数和类的定义&#xff0c;这…

C++设计模式:建造者模式(Builder) 房屋建造案例

什么是建造者模式&#xff1f; 建造者模式是一种创建型设计模式&#xff0c;它用于一步步地构建一个复杂对象&#xff0c;同时将对象的构建过程与它的表示分离开。简单来说&#xff1a; 它将复杂对象的“建造步骤”分成多部分&#xff0c;让我们可以灵活地控制这些步骤。通过…

设计模式-创建型-建造者模式

1.概念 建造者设计模式&#xff08;Builder Design Pattern&#xff09;是一种创建型设计模式&#xff0c;它通过将一个复杂对象的构建过程与它的表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。 2.作用 用于简化对复杂对象的创建 3.应用场景 当我们有一个非…

P1 练习卷(C++4道题)

1.纷繁世界 内存限制&#xff1a;256MB 时间限制&#xff1a;1s 问题描述 这是一个纷繁复杂的世界。 某一天清晨你起床很迟&#xff0c;没有吃上早饭。于是你骑着自行车去超市&#xff0c;但是你又发现商店的工作人员已经重新贴上了价格标签&#xff0c;零食价格都涨了50%。你…

Redis核心数据结构与高性能原理

一、Redis安装 下载地址&#xff1a;http://redis.io/download 安装步骤&#xff1a; # 安装gcc yum install gcc# 把下载好的redis-5.0.3.tar.gz放在/usr/local文件夹下&#xff0c;并解压 wget http://download.redis.io/releases/redis-5.0.3.tar.gz tar -zxvf redis-5.0.3…

深入理解 prompt提示词 原理及使用技巧

引言 在现代深度学习和人工智能领域&#xff0c;文本到图像生成模型&#xff08;如 Stable Diffusion、DALL-E 等&#xff09;已经取得了显著的进展。这些模型能够根据给定的文本提示词生成高质量的图像&#xff0c;极大地拓展了创意设计和艺术创作的可能性。然而&#xff0c;…

[高阶数据结构四] 初始图论

1.前言 本篇着重讲解图的相关知识&#xff0c;大家跟随我的脚步往下阅读。 本章重点&#xff1a; 本章着重讲解图的基本知识&#xff0c;图的存储结构&#xff1a;邻接矩阵&#xff0c;邻接表以及图的模拟实现 2.图的基本概念 图是由顶点集合及顶点间的关系组成的一种数据结构…

[OpenHarmony5.0][环境][教程]OpenHarmony 5.0源码在WSL2 Ubuntu22.04 编译环境搭建教程

F. 前言 教程基于OpenHarmony5.0 Release&#xff08;以下简称OHS_5&#xff09;&#xff0c;WSL2&#xff0c;Ubuntu22.04 为什么做这个教程&#xff1f;官方的文档写的比较乱&#xff0c;个人也是试了好久才搞出来环境。这里记录一下。 为什么用WSL&#xff1f;因为官方的…