广告
返回顶部
首页 > 资讯 > 后端开发 > Python >深入了解Java定时器中的Timer的原理
  • 795
分享到

深入了解Java定时器中的Timer的原理

2024-04-02 19:04:59 795人浏览 独家记忆

Python 官方文档:入门教程 => 点击学习

摘要

目录主要成员变量定时功能TimerThread结论Demo代码位置Java在1.3版本引入了Timer工具类,它是一个古老的定时器,搭配TimerTask和TaskQueue一起使用

Java在1.3版本引入了Timer工具类,它是一个古老的定时器,搭配TimerTask和TaskQueue一起使用。从Java5开始在并发包中引入了另一个定时器
ScheduledThreadPoolExecutor,它对Timer做了很多改进并提供了更多的工具,可以认为是对Timer的取代。

那为什么还要介绍Timer工具类呢?通过了解Timer的功能和它背后的原理,有助于我们更好的对比了解
ScheduledThreadPoolExecutor,同时ScheduledThreadPoolExecutor的一些改进思想在我们平时的编码工作中也可以借鉴。

主要成员变量

Timer中用到的主要是两个成员变量:

  • TaskQueue:一个按照时间优先排序的队列,这里的时间是每个定时任务下一次执行的毫秒数(相对于1970年1月1日而言)
  • TimerThread:对TaskQueue里面的定时任务进行编排和触发执行,它是一个内部无限循环的线程
//根据时间进行优先排序的队列    
private final TaskQueue queue = new TaskQueue();

//消费线程,对queue中的定时任务进行编排和执行
private final TimerThread thread = new TimerThread(queue);

//构造函数
public Timer(String name) {
        thread.setName(name);
        thread.start();
}

定时功能

Timer提供了三种定时模式:

  • 一次性任务
  • 按照固定的延迟执行(fixed delay)
  • 按照固定的周期执行(fixed rate)

第一种比较好理解,即任务只执行一次;针对第一种,Timer提供了以下两个方法:

//在当前时间往后delay个毫秒开始执行
public void schedule(TimerTask task, long delay) {...}
//在指定的time时间点执行
public void schedule(TimerTask task, Date time) {...}

第二种Fixed Delay模式也提供了以下两个方法

//从当前时间开始delay个毫秒数开始定期执行,周期是period个毫秒数
public void schedule(TimerTask task, long delay, long period) {...}
////从指定的firstTime开始定期执行,往后每次执行的周期是period个毫秒数
public void schedule(TimerTask task, Date firstTime, long period){...}

它的工作方式是:

第一次执行的时间将按照指定的时间点执行(如果此时TimerThread不在执行其他任务),如有其他任务在执行,那就需要等到其他任务执行完成才能执行。

从第二次开始,每次任务的执行时间是上一次任务开始执行的时间加上指定的period毫秒数。

如何理解呢,我们还是看代码

public static void main(String[] args) {
        TimerTask task1 = new DemoTimerTask("Task1");
        TimerTask task2 = new DemoTimerTask("Task2");
        Timer timer = new Timer();
        timer.schedule(task1, 1000, 5000);
        timer.schedule(task2, 1000, 5000);
}
    
static class DemoTimerTask extends TimerTask {
        private String taskName;
        private DateFORMat df = new SimpleDateFormat("HH:mm:ss---");
        
        public DemoTimerTask(String taskName) {
            this.taskName = taskName;
        }
        
        @Override
        public void run() {
            System.out.println(df.format(new Date()) + taskName + " is working.");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(df.format(new Date()) + taskName + " finished work.");
        }
}

task1和task2是几乎同时执行的两个任务,而且执行时长都是2秒钟,如果此时我们把第六行注掉不执行,我们将得到如下结果(和第三种Fixed Rate模式结果相同):

13:42:58---Task1 is working.
13:43:00---Task1 finished work.
13:43:03---Task1 is working.
13:43:05---Task1 finished work.
13:43:08---Task1 is working.
13:43:10---Task1 finished work.

如果打开第六行,我们再看下两个任务的执行情况。我们是期望两个任务能够同时执行,但是Task2是在Task1执行完成后才开始执行(原因是TimerThread是单线程的,每个定时任务的执行也在该线程内完成,当多个任务同时需要执行时,只能是阻塞了),从而导致Task2第二次执行的时间是它上一次执行的时间(13:43:57)加上5秒钟(13:44:02)。

13:43:55---Task1 is working.
13:43:57---Task1 finished work.
13:43:57---Task2 is working.
13:43:59---Task2 finished work.
13:44:00---Task1 is working.
13:44:02---Task1 finished work.
13:44:02---Task2 is working.
13:44:04---Task2 finished work.

那如果此时还有个Task3也是同样的时间点和间隔执行会怎么样呢?

结论是:也将依次排队,执行的时间依赖两个因素:

1.上次执行的时间

2.期望执行的时间点上有没有其他任务在执行,有则只能排队了

我们接下来看下第三种Fixed Rate模式,我们将上面的代码稍作修改:

public static void main(String[] args) {
        TimerTask task1 = new DemoTimerTask("Task1");
        TimerTask task2 = new DemoTimerTask("Task2");
        
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(task1, 1000, 5000);
        timer.scheduleAtFixedRate(task2, 1000, 5000);
}
    
static class DemoTimerTask extends TimerTask {
        private String taskName;
        private DateFormat df = new SimpleDateFormat("HH:mm:ss---");
        
        public DemoTimerTask(String taskName) {
            this.taskName = taskName;
        }
        
        @Override
        public void run() {
            System.out.println(df.format(new Date()) + taskName + " is working.");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(df.format(new Date()) + taskName + " finished work.");
        }
}

Task1和Task2还是在相同的时间点,按照相同的周期定时执行任务,我们期望Task1能够每5秒定时执行任务,期望的时间点是:14:21:47-14:21:52-14:21:57-14:22:02-14:22:07,实际上它能够交替着定期执行,原因是Task2也会定期执行,并且对TaskQueue的他们是交替着拿的(这个在下面分析TimerThread源码的时候会讲到)

14:21:47---Task1 is working.
14:21:49---Task1 finished work.
14:21:49---Task2 is working.
14:21:51---Task2 finished work.
14:21:52---Task2 is working.
14:21:54---Task2 finished work.
14:21:54---Task1 is working.
14:21:56---Task1 finished work.
14:21:57---Task1 is working.
14:21:59---Task1 finished work.
14:21:59---Task2 is working.
14:22:01---Task2 finished work.

TimerThread

上面我们主要讲了Timer的一些主要源码及定时模式,下面我们来分析下支撑Timer的定时任务线程TimerThread。

TimerThread大概流程图如下:

TimerThread流程

源码解释如下:

private void mainLoop() {
        while (true) {
            try {
                TimerTask task;
                boolean taskFired;
                synchronized(queue) {
                    // 如果queue里面没有要执行的任务,则挂起TimerThread线程
                    while (queue.isEmpty() && newTasksMayBeScheduled)
                        queue.wait();
                    // 如果TimerThread被激活,queue里面还是没有任务,则介绍该线程的无限循环,不再接受新任务
                    if (queue.isEmpty())
                        break; 

                    long currentTime, executionTime;
                    // 获取queue队列里面下一个要执行的任务(根据时间排序,也就是接下来最近要执行的任务)
                    task = queue.getMin();
                    synchronized(task.lock) {
                        if (task.state == TimerTask.CANCELLED) {
                            queue.removeMin();
                            continue;  // No action required, poll queue again
                        }
                        currentTime = System.currentTimeMillis();
                        executionTime = task.nextExecutionTime;
                        // taskFired表示是否需要立刻执行线程,当task的下次执行时间到达当前时间点时为true
                        if (taskFired = (executionTime<=currentTime)) {
                            //task.period==0表示这个任务只需要执行一次,这里就从queue里面删掉了
                            if (task.period == 0) { 
                                queue.removeMin();
                                task.state = TimerTask.EXECUTED;
                            } else { // Repeating task, reschedule
                                //针对task.period不等于0的任务,则计算它的下次执行时间点
                                //task.period<0表示是fixed delay模式的任务
                                //task.period>0表示是fixed rate模式的任务
                                queue.rescheduleMin(
                                  task.period<0 ? currentTime   - task.period
                                                : executionTime + task.period);
                            }
                        }
                    }
                    // 如果任务的下次执行时间还没有到达,则挂起TimerThread线程executionTime - currentTime毫秒数,到达执行时间点再自动激活
                    if (!taskFired) 
                        queue.wait(executionTime - currentTime);
                }
                // 如果任务的下次执行时间到了,则执行任务
                // 注意:这里任务执行没有另起线程,还是在TimerThread线程执行的,所以当有任务在同时执行时会出现阻塞
                if (taskFired)  
                    // 这里没有try catch异常,当TimerTask抛出异常会导致整个TimerThread跳出循环,从而导致Timer失效
                    task.run();
            } catch(InterruptedException e) {
            }
        }
}

结论

通过上面的分析,我们可以得出以下结论:

  • Timer支持三种模式的定时任务(一次性任务,Fixed Delay模式,Fixed Rate模式)
  • Timer中的TimerThread是单线程模式,因此导致所有定时任务不能同时执行,可能会出现延迟
  • TimerThread中并没有处理好任务的异常,因此每个TimerTask的实现必须自己try catch防止异常抛出,导致Timer整体失效

Demo代码位置

TimerFixedRateDemo.java

TimerFixedDelayDemo.java

到此这篇关于深入了解Java定时器中的Timer的原理的文章就介绍到这了,更多相关Java Timer原理内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 深入了解Java定时器中的Timer的原理

本文链接: https://www.lsjlt.com/news/162202.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 深入了解Java定时器中的Timer的原理
    目录主要成员变量定时功能TimerThread结论Demo代码位置Java在1.3版本引入了Timer工具类,它是一个古老的定时器,搭配TimerTask和TaskQueue一起使用...
    99+
    2022-11-12
  • 怎么深入Java Timer 定时任务调度器实现原理
    这篇文章将为大家详细讲解有关怎么深入Java Timer 定时任务调度器实现原理,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。使用 Java 来调度定时任务时,我们经常会使用 Timer 类...
    99+
    2023-06-02
  • Java中的定时器Timer详解
    目录总结简单来说,定时器就相当于一个“闹钟”,给定时器设定一个任务,约定这个任务在xxx时间之后执行~ Timer类提供了一个核心接口,schedule(安排) 指定一个任...
    99+
    2022-11-12
  • GoLang中的timer定时器实现原理分析
    // NewTimer creates a new Timer that will send // the current time on its channel after at ...
    99+
    2023-02-02
    Go timer定时器 Go timer Go定时器
  • Java多线程定时器Timer原理的示例分析
    这篇文章给大家分享的是有关Java多线程定时器Timer原理的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。Timer的schedule(TimeTask task, Date time)的使用该方法的作...
    99+
    2023-05-30
    java
  • golang定时器Timer的用法和实现原理解析
    目录一文搞懂golang定时器Timer的用法和实现原理前言Timertimer结构体创建定时器停止定时器重置定时器实现原理数据结构runtimeTimer创建Timer停止Time...
    99+
    2023-05-15
    golang定时器 golang定时器Ticker
  • 深入浅析Java中的定时器
    今天就跟大家聊聊有关深入浅析Java中的定时器,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。定时器问题  定时器属于基本的基础组件,不管是用户空间的程序开发,还是内核空间的程序开发,...
    99+
    2023-05-31
    java 定时器 ava
  • 深入了解Vue中双向数据绑定原理
    目录数据的变化反应到视图命令式操作视图声明式操作视图小结视图的变化反应到数据现存的问题数据的变化反应到视图 前面我们了解到数据劫持之后,我们可以在数据发生修改之后做任何我们想要做的事...
    99+
    2022-11-13
  • 深入了解vuex的实现原理
    当面试被问vuex的实现原理,你要怎么回答?下面本篇文章就来带大家深入了解一下vuex的实现原理,希望对大家有所帮助!关于vuex就不再赘述,简单回顾一下:当应用碰到多个组件共享状态时,简单的单向数据流很容易被破坏:第一,多个视图依赖于同一...
    99+
    2023-05-14
    javascript vuex
  • 深入了解Vue3中侦听器watcher的实现原理
    目录watch API 的用法watch API实现原理标准化source构造回调函数创建scheduler创建effect返回销毁函数异步任务队列的设计异步任务队列的创建异步任务队...
    99+
    2022-11-13
  • 深入了解Android IO的底层原理
    目录前言一、应用层1. IO的分类1.1 缓冲和直接1.2 阻塞和异步2. IO流程二、sysCall系统调用三、虚拟文件系统1. VFS结构2. VFS中的缓存3. IO流程四、文...
    99+
    2022-11-13
  • 深入了解Vue3中props的原理与使用
    目录前言介绍原理前提创建组件实例对象初始化Props操作创建proxy对象去获取Propsprops作为参数传入setup将proxy挂载到render上总结前言 props指父组件...
    99+
    2023-05-19
    Vue3 props原理 Vue3 props使用 Vue3 props
  • 深入了解Java中finalize方法的作用和底层原理
    目录finalize方法是什么finalize方法与C++的析构函数的区别finalize方法合适清理的对象可以触发finalize执行的方法finalize实现对象再生问题fina...
    99+
    2022-12-29
    Java finalize方法作用 Java finalize方法原理 Java finalize方法 Java finalize
  • 深入了解Python中的时间处理函数
    目录一、datetime模块介绍1 datetime.date类2 datetime.datetime类3 datetime.timedelta类二、日期转字符三、字符转日期四、数值...
    99+
    2022-11-12
  • Java代理模式的深入了解
    目录一、静态代理模式1.1、 代理模式的定义:1.2、代理模式的优缺点二、动态代理模式总结一、静态代理模式 1.1、 代理模式的定义: 由于某些原因需要给某对象提供一个代理以控制对该...
    99+
    2022-11-12
  • 深入了解MySQL中索引优化器的工作原理
    目录本文导读一、mysql 优化器是如何选择索引的1、MySQL数据库组成2、MySQL数据库成本计算二、MySQL查询成本三、SELECT 执行过程总结本文导读 本文将解读MySQL数据库查询优化器(CBO)的...
    99+
    2022-11-09
  • golang定时器Timer的用法和实现原理是什么
    本篇内容介绍了“golang定时器Timer的用法和实现原理是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!TimerTimer是一种单...
    99+
    2023-07-06
  • Java 中Timer和TimerTask 定时器和定时任务使用的例子
    这两个类使用起来非常方便,可以完成我们对定时器的绝大多数需求Timer类是用来执行任务的类,它接受一个TimerTask做参数Timer有两种执行任务的模式,最常用的是schedule,它可以以两种方式执行任务:1:在某个时间(Data),...
    99+
    2023-05-31
    timertask 定时器 tim
  • java中Timer定时器的使用和启动方式
    目录Timer定时器的使用和启动1.概述2.应用场景3.使用方法4.启动方法java的几种定时器小结1.@Scheduled注解2.quartz3.使用Timer4.使用线程控制Ti...
    99+
    2022-11-12
  • 深入了解Java8中的时区日期时间
    目录Java8Tester.java处理时区上一章节 Java 8 新日期时间 API ( 上 ) – 本地日期时间 我们对 Java 8 重新设计的日期时间 API 做...
    99+
    2023-05-14
    Java8时区日期时间 Java8 日期时间 Java8时区
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作