Java | 一分钟掌握定时任务 | 3 - 单机定时之Timer

news2025/1/21 18:45:02

作者:Mars酱

声明:本文章由Mars酱原创,部分内容来源于网络,如有疑问请联系本人。

转载:欢迎转载,转载前先请联系我!

介绍

这个是个JDK远古时代的api了,据考证,可以追溯到JDK 1.3 的时候,历史就不讲了,毕竟我们不是技术考古。还是按照我们以前的方式,废话少说直接上车吧。

简单使用

我们先直接new一个Timer对象:

java.util.Timer timer = new Timer();

然后调用时,发现可用的方法并不多:

根据多年算命经验,我猜既然是定时任务,那么包含关键字schedule的就是核心方法了,挑个最简单的写下来:

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/**
 * @author mars酱
 */
public class MarsTimer {
    public static void main(String[] args) {
        java.util.Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("当前时间:" + new Date());
            }
        }, 1000, 5000);
    }
}

运行了一下,得到了个结果:

结果是每五秒执行一次输出。

Timer的schedule函数有三个参数:

TimerTask:这个就是需要安排的任务对象了,可以new一个之后像我一样实现它的run方法;

delay:在任务执行之前延迟多久,单位是毫秒;

period:每次执行之间的间隔多久,单位也是毫秒;

好了,就是这么简单,感觉再复杂也复杂不到哪里去了

会不会有阻塞?

我们理想的定时任务,应该是多个定时器同步并发执行,各自之间不受影响,对吧?那我们试试Timer会不会有这样的情况吧。

我们假设两个任务,一个任务在执行的过程中导致某些不可抗拒的原因,延迟了5秒,那么另一个任务的执行是不是会被这个延迟的任务影响,理想的情况下的两个定时任务各自完成自己任务,互不干涉和影响。

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/**
 * @author mars酱
 */
public class MarsTimer {
    public static void main(String[] args) {
//        java.util.Timer timer = new Timer();
//        timer.schedule(new TimerTask() {
//            @Override
//            public void run() {
//                System.out.println("当前时间:" + new Date());
//            }
//        }, 1000, 5000);

        // 1. 创建第一个任务,打印时间后延迟5秒
        TimerTask tta = new TimerTask() {
            @SneakyThrows
            @Override
            public void run() {
                System.out.println(">> 这是a任务:当前时间:" + new Date());
                Thread.sleep(5000);
            }
        };

        // 2. 创建第二个任务,直接打印毫秒数
        TimerTask ttb = new TimerTask() {
            @Override
            public void run() {
                System.out.println("<< 这是b任务:当前毫秒:" + System.currentTimeMillis());
            }
        };

        Timer timera = new Timer();
        // 3. 把两个任务都加入计时器中
        timera.schedule(tta, 1000, 5000);
        timera.schedule(ttb, 1000, 5000);
    }
}

运行一下,得到结果:

看出什么来了吗?是不是从结果中没看明白?我就知道,那么我们去掉那段Thread.sleep,得到的结果类似这样:

对比一下上下两个结果,这下是不是明白了?由于a任务的延迟,导致了b任务受到了影响。看来JDK的定时任务是存在阻塞情况的,b任务执行只能等待a完成之后才能执行。

怎么解决?

先不解决,那是下一章节的内容,哈哈哈哈

其他方法

我们先介绍完Timer和TimerTask吧。

TimerTask是个抽象类,依赖Runnable接口,实现TimerTask,我们只要实现run函数就行了,就像这样:

import java.util.TimerTask;

/**
 * @author mars酱
 */
public class DefaultTimerTask extends TimerTask {
    @Override
    public void run() {
        // 你的业务实现
    }
}

原理

翻看java.util.Timer的源代码,核心点如下:

    /**
     * Schedule the specified timer task for execution at the specified
     * time with the specified period, in milliseconds.  If period is
     * positive, the task is scheduled for repeated execution; if period is
     * zero, the task is scheduled for one-time execution. Time is specified
     * in Date.getTime() format.  This method checks timer state, task state,
     * and initial execution time, but not period.
     *
     * @throws IllegalArgumentException if <tt>time</tt> is negative.
     * @throws IllegalStateException if task was already scheduled or
     *         cancelled, timer was cancelled, or timer thread terminated.
     * @throws NullPointerException if {@code task} is null
     */
    private void sched(TimerTask task, long time, long period) {
        if (time < 0)
            throw new IllegalArgumentException("Illegal execution time.");

        // Constrain value of period sufficiently to prevent numeric
        // overflow while still being effectively infinitely large.
        if (Math.abs(period) > (Long.MAX_VALUE >> 1))
            period >>= 1;

        synchronized(queue) {
            if (!thread.newTasksMayBeScheduled)
                throw new IllegalStateException("Timer already cancelled.");

            synchronized(task.lock) {
                if (task.state != TimerTask.VIRGIN)
                    throw new IllegalStateException(
                        "Task already scheduled or cancelled");
                task.nextExecutionTime = time;
                task.period = period;
                task.state = TimerTask.SCHEDULED;
            }

            queue.add(task);
            if (queue.getMin() == task)
                queue.notify();
        }
    }

这段大致的意思就是往Timer内部的一个队列里面塞任务,塞的时候做了很多检查,比如周期、任务状态等等,用到了关键字synchronized,那么说明这个队列是个资源共享的,每次操作的时候必须一下,那么我们再看下Timer在执行队列的部分源代码:

    /**
     * The main timer loop.  (See class comment.)
     */
    private void mainLoop() {
        while (true) {
            try {
                TimerTask task;
                boolean taskFired;
                synchronized(queue) {
                    // Wait for queue to become non-empty
                    while (queue.isEmpty() && newTasksMayBeScheduled)
                        queue.wait();
                    if (queue.isEmpty())
                        break; // Queue is empty and will forever remain; die

                    // Queue nonempty; look at first evt and do the right thing
                    long currentTime, executionTime;
                    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;
                        if (taskFired = (executionTime<=currentTime)) {
                            if (task.period == 0) { // Non-repeating, remove
                                queue.removeMin();
                                task.state = TimerTask.EXECUTED;
                            } else { // Repeating task, reschedule
                                queue.rescheduleMin(
                                  task.period<0 ? currentTime   - task.period
                                                : executionTime + task.period);
                            }
                        }
                    }
                    if (!taskFired) // Task hasn't yet fired; wait
                        queue.wait(executionTime - currentTime);
                }
                if (taskFired)  // Task fired; run it, holding no locks
                    task.run();
            } catch(InterruptedException e) {
            }
        }
    }

Timer在构造函数中就调用了start函数,start函数底层可以理解为用jvm去调用run函数,而这个mainLoop函数是Timer函数的实现部分,所以,最终Timer的执行都是在这里了。大致的执行顺序解释:

  1. 检查队列的情况,是不是空,是不是状态正常;
  2. 当前任务的状态是不是可以执行;
  3. 当前任务是不是已经到了可以触发的时间了
  4. 以上情况都ok,那就执行我们之前实现TimerTask的run函数

以上四点过程,全程都用synchronized包裹。

总结

Timer和TimerTask是JDK原生提供的定时任务实现方案,简单易用,但是平时的场景也经常会有业务处理过程较长的情况,但是单个任务的执行不能影响其他任务的定时情况,所以如果处理定时任务的并发,我们可以使用线程池,下个站见。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/527283.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CSS小技巧之圆形虚线边框

虚线相信大家日常都用的比较多&#xff0c;常见的用法就是使用 border-style 控制不同的样式&#xff0c;比如设置如下边框代码&#xff1a; border-style: dotted dashed solid double;这将设置顶部的边框样式为点状&#xff0c;右边的边框样式为虚线&#xff0c;底部的边框样…

视频采集到录制 - 采集到显示碰到一些难点

项目中用到相机后端处理&#xff0c;走了一些弯路&#xff0c;也遇到不少问题&#xff08;解决了不少问题&#xff09;&#xff0c;特意写下本文记录下当时点点滴滴。 讲一下背景&#xff0c;公司自研相机&#xff0c;用于一些高端场合&#xff0c;因此对后端处理也非常讲究 …

网络基本知识分享

目录 1.IP地址 2.端口号 3.协议 4.协议分层 5.Tcp/Ip五层网络模型 5.1 应用层 5.2 传输层 5.3 网络层 5.4 数据链路层 5.5 物理层 6.封装和分用 6.1 封装 6.1.1 应用层拿到数据 6.1.2 向下传递给传输层 6.1.3 继续向下传递给网络层 6.1.4 继续向下传递给数据链…

【自制视频课程】C++OpnecV基础35讲——第一章 前言

为什么要学习OpenCV&#xff1f; 首先&#xff0c;opencv是一个广泛使用的计算机视觉库&#xff0c;它提供了丰富的图像处理和计算机视觉算法&#xff0c;可以帮助我们快速地开发出高质量的图像处理应用程序&#xff1b; 其次&#xff0c;opencv是一个开源库&#xff0c;可以免…

Spark大数据处理讲课笔记4.3 Spark SQL数据源 - Parquet文件

文章目录 零、本讲学习目标一、Parquet概述二、读取和写入Parquet的方法&#xff08;一&#xff09;利用parquet()方法读取parquet文件1、读取parquet文件2、显示数据帧内容 &#xff08;二&#xff09;利用parquet()方法写入parquet文件1、写入parquet文件2、查看生成的parque…

零入门kubernetes网络实战-32->基于路由技术+brigde+veth pair形成的跨主机通信方案

《零入门kubernetes网络实战》视频专栏地址 https://www.ixigua.com/7193641905282875942 本篇文章视频地址(稍后上传) 本文主要使用的技术是 路由技术Linux虚拟网桥虚拟网络设备veth pair来实现跨主机通信 该方案是flannel网络方案中的host-gw网络模型的基础。 1、总结 本…

化制为智,驭数前行 | 如何把握油气装备领域智能制造的未来?

01「智」赋未来&#xff0c;油燃而升 2015年&#xff0c;我国提出了“中国制造2025”规划&#xff0c;把智能制造作为两化深度融合的主攻方向&#xff0c;智能制造产业链所蕴藏的巨大投资机会将逐渐被市场挖掘。作为国家战略的基础&#xff0c;油气工程装备&#xff0c;特别是…

C++ 基础STL-list容器

STL-list 容器&#xff0c;又称双向链表容器&#xff0c;即该容器的底层是以双向链表的形式实现的。这意味着&#xff0c;list 容器中的元素可以分散存储在内存空间里&#xff0c;而不是必须存储在一整块连续的内存空间中。 链表的优点&#xff1a;可以对任意位置进行快速插入和…

【触觉智能分享】RK3568+Debian系统如何旋转屏幕显示方向

大家在购买开发板后&#xff0c;默认开机进入桌面屏幕显示方向是竖屏&#xff0c;有些用户想修改为横屏显示&#xff0c;本文就用IDO-EVB3568为例&#xff0c;用Debian系统演示如何旋转屏幕显示方向&#xff0c;此方法适用于所有RK356X系列产品。 IDO-EVB3568开发板拥有四核A5…

【数据结构】--- 几分钟走进栈和队列(详解-上)

文章目录 前言&#x1f31f;一、栈&#x1f30f;1.1栈的概念及结构&#xff1a;&#x1f30f;1.2实现栈的两种方式&#xff1a; &#x1f31f;二、栈实现(数组栈)&#x1f30f;2.1结构&#xff1a;&#x1f30f;2.2初始化&#xff1a;&#x1f4ab;2.2.1第一种代码&#xff1a;…

Direct3D 12——纹理——纹理贴图的作用

法线贴图 在不增加三角形的情况下增加表面细节 任何一个像素它的法线都做一个扰动&#xff0c;通过定义不同的高度和临近位置的高度差重新计算法线 纹理定义的是任何一个点&#xff0c;它的相对的高度的移动&#xff0c;通过高度的变化改变法线 原曲面法向量n ( p) (0,1) p点…

代码随想录 LeetCode链表篇 Java

文章目录 &#xff08;简单&#xff09;203. 移除链表元素&#xff08;中等&#xff09;707. 设计链表&#xff08;简单&#xff09;206. 反转链表&#xff08;中等&#xff09;24. 两两交换链表中的节点&#xff08;中等&#xff09;19. 删除链表的倒数第 N 个结点&#xff08…

Schlumberger ECLIPSE CRACK

Schlumberger ECLIPSE CRACK 工业和工程软件旨在模拟Schlumberger ECLIPSE Simulation的碳氢化合物&#xff0c;该模拟与Shelberger的技术服务有关&#xff0c;以及用于预测和历史的容器中当前方程数量的最新和最新解决方案集。动态行为是各种传统和不寻常的石油和天然气模式。…

DevExpress:报表控件绑定数据库数据源的三种方式(Winform)

1.写在前面 如果你是和我一样&#xff0c;第一次接触DevExpress&#xff0c;并且因为网上资源眼花缭乱无从下手&#xff0c;然后脑子一转直接到DevExpress官网寻找官方使用文档的&#xff0c;那我们的了解顺序应该差不多是一致的。 DevExpress官网&#xff1a;https://www.de…

【笔试强训选择题】Day12.习题(错题)解析

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;笔试强训选择题 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01; 文章目录…

Facebook 手机应用广告:在移动时代实现营销突破

在移动时代&#xff0c;手机已经成为人们生活的重要组成部分。随着移动互联网的普及&#xff0c;人们更频繁地使用手机来浏览社交媒体、获取信息和进行购物。 对于企业而言&#xff0c;如何在移动平台上实现营销突破&#xff0c;吸引用户的注意力和提升品牌价值&#xff0c;是…

数说热点|社恐人群运动健身指南:不想去健身房,那就在家找面墙

连杰伦都开始跳操了&#xff0c;你还不动动动动动起来&#xff1f; 随着《运动者联盟》这档体育挑战真人秀节目的完美收官&#xff0c;忙碌生活中的运动激情似乎又被点燃了。5月9日&#xff0c;周杰伦现身厦门&#xff0c;在活动现场和刘耕宏合体跳起了《本草纲目》&#xff0…

音视频技术开发周刊 | 293

每周一期&#xff0c;纵览音视频技术领域的干货。 新闻投稿&#xff1a;contributelivevideostack.com。 谷歌全面反攻 ChatGPT&#xff01;PaLM 2、Gemini 双杀&#xff0c;Bard 正式开放 以上是2023 Google I/O 大会的重点内容&#xff0c;AI含量极高。 谷歌推拥有26000个H10…

uni-app框架的小程序开发环境

文章目录 一、下载微信开发工具安装 二、构建uni-app开发环境2.1 Node.js下载与安装2.2 下载HBuilder-X2.3 创建uni-app项目2.4 填入uni-app专属标识和小程序标识 在阅读此博文前&#xff0c;需要注册完毕小程序账号 一、下载微信开发工具 微信开发工具下载地址 下载稳定版即…

代码随想录算法训练营day41 | 343. 整数拆分,96.不同的二叉搜索树

代码随想录算法训练营day41 | 343. 整数拆分&#xff0c;96.不同的二叉搜索树 343. 整数拆分解法一&#xff1a;动态规划 96.不同的二叉搜索树解法一&#xff1a;动态规划 总结 343. 整数拆分 教程视频&#xff1a;https://www.bilibili.com/video/BV1Mg411q7YJ 1、dp[i]代表…