当前位置: 首页 > news >正文

Java多线程:定时器Timer

前言

定时/计划功能在Java应用的各个领域都使用得非常多,比方说Web层面,可能一个项目要定时采集话单、定时更新某些缓存、定时清理一批不活跃用户等等。定时计划任务功能在Java中主要使用的就是Timer对象,它在内部使用多线程方式进行处理,所以它和多线程技术关联还是相当大的。那和ThreadLocal一样,还是先讲原理再讲使用,Timer的实现原理不难,就简单扫一下就好了。

Timer的schedule(TimeTask task, Date time)的使用

该方法的作用是在执行的日期执行一次任务

1、执行任务的时间晚于当前时间:未来执行

private static Timer timer = new Timer();static public class MyTask extends TimerTask {public void run() {System.out.println("运行了!时间为:" + new Date());}}public static void main(String[] args) throws Exception {MyTask task = new MyTask();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String dateString = "2015-10-6 12:14:00";Date dateRef = sdf.parse(dateString);System.out.println("字符串时间:" + dateRef.toLocaleString() + " 当前时间:" + new Date().toLocaleString());timer.schedule(task, dateRef);}

看一下运行效果:

字符串时间:2015-10-6 12:14:00 当前时间:2015-10-6 12:13:23

运行了!时间为:Tue Oct 06 12:14:00 CST 2015

执行时间和但前时间不一致,而是和dateRef的时间一直,证明了未来执行。任务虽然执行完了,但进程没有销毁,控制台上的方框可以看到还是红色的,看下Timer的源代码:

public Timer() {this("Timer-" + serialNumber());}public Timer(String name) {thread.setName(name);thread.start();}

所以,启动一个Timer就是启动一个新线程,但是这个新线程并不是守护线程,所以它会一直运行。要运行完就让进程停止的话,设置Timer为守护线程就好了,有专门的构造函数可以设置:

public Timer(boolean isDaemon) {this("Timer-" + serialNumber(), isDaemon);}public Timer(String name, boolean isDaemon) {thread.setName(name);thread.setDaemon(isDaemon);thread.start();}

2、计划时间早于当前时间:立即执行

如果执行任务的时间早于当前时间,那么立即执行task的任务:

private static Timer timer = new Timer();static public class MyTask extends TimerTask {public void run() {System.out.println("运行了!时间为:" + new Date());}}public static void main(String[] args) throws Exception {MyTask task = new MyTask();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String dateString = "2014-10-6 12:14:00";Date dateRef = sdf.parse(dateString);System.out.println("字符串时间:" + dateRef.toLocaleString() + " 当前时间:" + new Date().toLocaleString());timer.schedule(task, dateRef);}

看一下运行效果:

字符串时间:2014-10-6 12:14:00 当前时间:2015-10-6 12:20:10

运行了!时间为:Tue Oct 06 12:20:10 CST 2015

执行时间和当前时间一致,证明了立即执行

3、多个TimerTask任务执行

Timer中允许有多个任务:

private static Timer timer = new Timer();static public class MyTask extends TimerTask {public void run() {System.out.println("运行了!时间为:" + new Date());}}public static void main(String[] args) throws Exception {MyTask task1 = new MyTask();MyTask task2 = new MyTask();SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String dateString1 = "2015-10-6 12:26:00";String dateString2 = "2015-10-6 12:27:00";Date dateRef1 = sdf1.parse(dateString1);Date dateRef2 = sdf2.parse(dateString2);System.out.println("字符串时间:" + dateRef1.toLocaleString() + " 当前时间:" + new Date().toLocaleString());System.out.println("字符串时间:" + dateRef2.toLocaleString() + " 当前时间:" + new Date().toLocaleString());timer.schedule(task1, dateRef1);timer.schedule(task2, dateRef2);}

看一下运行结果:

字符串时间:2015-10-6 12:26:00 当前时间:2015-10-6 12:25:38

字符串时间:2015-10-6 12:27:00 当前时间:2015-10-6 12:25:38

运行了!时间为:Tue Oct 06 12:26:00 CST 2015

运行了!时间为:Tue Oct 06 12:27:00 CST 2015

可以看到,运行时间和设置的时间一致,证明了未来可以执行多个任务。另外注意,Task是以队列的方式一个一个被顺序执行的,所以执行的时间有可能和预期的时间不一致,因为前面的任务可能消耗过长,后面任务的运行时间也有可能被延迟。

代码就不写了,举个例子,任务1计划12:00:00被执行,任务2计划12:00:10被执行,结果任务1执行了30秒,那么任务2将在12:00:30被执行,因为Task是被放入队列中的,因此必须一个一个顺序运行。

Timer的schedule(TimerTask task, Date firstTime, long period)

该方法的作用是在指定的日期之后,按指定的间隔周期性地无限循环地执行某一人物

1、计划时间晚于当前时间:未来执行

 static public class MyTask extends TimerTask {public void run() {System.out.println("运行了!时间为:" + new Date());}}public static void main(String[] args) throws Exception {MyTask task = new MyTask();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String dateString = "2015-10-6 18:00:00";Timer timer = new Timer();Date dateRef = sdf.parse(dateString);System.out.println("字符串时间:" + dateRef.toLocaleString() + " 当前时间:" + new Date().toLocaleString());timer.schedule(task, dateRef, 4000);}

看一下运行结果:

字符串时间:2015-10-6 18:01:00 当前时间:2015-10-6 18:00:15

运行了!时间为:Tue Oct 06 18:01:00 CST 2015

运行了!时间为:Tue Oct 06 18:01:04 CST 2015

运行了!时间为:Tue Oct 06 18:01:08 CST 2015

运行了!时间为:Tue Oct 06 18:01:12 CST 2015

...

看到从设定的时间开始,每隔4秒打印一次,无限打印下去

2、计划时间早于当前时间:立即执行

static public class MyTask extends TimerTask {public void run() {System.out.println("运行了!时间为:" + new Date());}}public static void main(String[] args) throws Exception {MyTask task = new MyTask();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String dateString = "2014-10-6 18:01:00";Timer timer = new Timer();Date dateRef = sdf.parse(dateString);System.out.println("字符串时间:" + dateRef.toLocaleString() + " 当前时间:" + new Date().toLocaleString());timer.schedule(task, dateRef, 4000);}

看一下运行结果:

字符串时间:2014-10-6 18:01:00 当前时间:2015-10-6 18:02:46

运行了!时间为:Tue Oct 06 18:02:46 CST 2015

运行了!时间为:Tue Oct 06 18:02:50 CST 2015

运行了!时间为:Tue Oct 06 18:02:54 CST 2015

运行了!时间为:Tue Oct 06 18:02:58 CST 2015

运行了!时间为:Tue Oct 06 18:03:02 CST 2015

...

看到运行时间比当前时间早,从当前时间开始,每隔4秒打印一次,无限循环下去

TimerTask的cancel()方法

TimerTask的cancel()方法的作用是将自身从任务队列中清除:

 static public class MyTaskA extends TimerTask {public void run() {System.out.println("A运行了!时间为:" + new Date());this.cancel();}}static public class MyTaskB extends TimerTask {public void run() {System.out.println("B运行了!时间为:" + new Date());}}public static void main(String[] args) throws Exception {MyTaskA taskA = new MyTaskA();MyTaskB taskB = new MyTaskB();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String dateString = "2015-10-6 18:10:00";Timer timer = new Timer();Date dateRef = sdf.parse(dateString);System.out.println("字符串时间:" + dateRef.toLocaleString() + " 当前时间:" + new Date().toLocaleString());timer.schedule(taskA, dateRef, 4000);timer.schedule(taskB, dateRef, 4000);}

看一下运行结果:

字符串时间:2015-10-6 18:10:00 当前时间:2015-10-6 18:09:47

A运行了!时间为:Tue Oct 06 18:10:00 CST 2015

B运行了!时间为:Tue Oct 06 18:10:00 CST 2015

B运行了!时间为:Tue Oct 06 18:10:04 CST 2015

B运行了!时间为:Tue Oct 06 18:10:08 CST 2015

B运行了!时间为:Tue Oct 06 18:10:12 CST 2015

...

看到TimeTask的cancel()方法是将自身从任务队列中被移除,其他任务不受影响

Timer的cancel()方法

把上面代码改动一下:

private static Timer timer = new Timer();static public class MyTaskA extends TimerTask {public void run() {System.out.println("A运行了!时间为:" + new Date());timer.cancel();}}static public class MyTaskB extends TimerTask {public void run() {System.out.println("B运行了!时间为:" + new Date());}}public static void main(String[] args) throws Exception {MyTaskA taskA = new MyTaskA();MyTaskB taskB = new MyTaskB();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String dateString = "2015-10-6 18:10:00";Date dateRef = sdf.parse(dateString);System.out.println("字符串时间:" + dateRef.toLocaleString() + " 当前时间:" + new Date().toLocaleString());timer.schedule(taskA, dateRef, 4000);timer.schedule(taskB, dateRef, 4000);}

看一下运行结果:

字符串时间:2015-10-6 18:10:00 当前时间:2015-10-6 18:14:15

A运行了!时间为:Tue Oct 06 18:14:15 CST 2015

全部任务都被清除,并且进程被销毁。不过注意一下,cancel()方法未必一定会停止执行计划任务,可能正常执行,因为cancel()方法会尝试去获取queue锁,如果并没有获取到queue锁的话,TimerTask类中的任务继续执行也是完全有可能的

其他方法

再列举一些Timer中的其他schedule的重载方法的作用,就不提供证明的代码了,可以自己尝试一下:

1、schedule(TimerTask task, long delay)

以当前时间为参考,在此时间基础上延迟指定的毫秒数后执行一次TimerTask任务

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

以当前时间为参考,在此时间基础上延迟指定的毫秒数后,以period为循环周期,循环执行TimerTask任务

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

在延时的场景下,schedule方法和scheduleAtFixedRate方法没有区别,它们的区别只是在非延时上。如果执行任务的时间没有被延时,对于schedule方法来说,下一次任务执行的时间参考的是上一次任务的开始时间来计算的;对于scheduleAtFixedRate方法来说,下一次任务执行的时间参考的是上一次任务的结束时间来计算的

相关文章:

Java多线程:定时器Timer

前言 定时/计划功能在Java应用的各个领域都使用得非常多,比方说Web层面,可能一个项目要定时采集话单、定时更新某些缓存、定时清理一批不活跃用户等等。定时计划任务功能在Java中主要使用的就是Timer对象,它在内部使用多线程方式进行处理&am…...

设计模式---装饰模式

目录 介绍 实现 优缺点 装饰模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有类的一个包装。这种模式创建了一个装饰类,用来包装原有…...

跨时钟域传输数据——单bit和多bit信号(总结)

文章目录前言一、慢时钟域到快时钟域1、单bit信号2、多bit信号二、快时钟域到慢时钟域1、单bit信号2、多bit信号三、多bit信号跨时钟域传输1、多个信号合并2、多周期路径 Multi-cycle Path/MCP3、使用格雷码4、使用异步FIFO5、使用DMUX电路结构6、握手信号传输四、简答题1、跨时…...

高并发下如何保证接口幂等

文章目录 1. insert前先select2. 加悲观锁3. 加乐观锁4. 加唯一索引5. 建防重表6. 根据状态机7. 加分布式锁8. 获取token接口幂等性问题,对于开发人员来说,是一个跟语言无关的公共问题。本文分享了一些解决这类问题非常实用的办法,绝大部分内容我在项目中实践过的,给有需要…...

Retrofit源码分析小结

Retrofit源码分析&小结 简介 Retrofit是对Okhttp网络请求的二次封装,通过注解动态代理的方式,简化了Okhttp的使用,使得通过简单的配置就可以像调用接口一样去请求网络接口;除此之外Retrofit还支持RxJava和kotlin的协程 基本…...

【从零开始学习 UVM】11.4、UVM Register Layer —— UVM Register Model 实战项目(RAL实战,交通灯为例)

文章目录 DesignInterfaceRegister Model ExampleRegister EnvironmentAPB Agent ExampleTestbench EnvironmentSequencesTest在之前的几篇文章中,我们已经了解了寄存器模型是什么以及如何使用它来访问给定设计中的寄存器。现在让我们看一个完整的例子,展示如何为给定设计编写…...

session和token的登录机制

做登录的时候遇到了token ,web和smtp的登录情况,这里 记录一下我所学习的两种登录方式,一种是token ,一种是session session 登录机制 当用户请求登录接口进行登录服务端 获得登录的信息,从而在数据库中查到相应的用…...

大厂研发成本大曝光,研发占大头

近日,腾讯发布《2022 年腾讯研发大数据报告》,披露了 2022 年腾讯在研发投入、研发效能、开源协同等方面的重要数据。 《报告》显示,2022 年腾讯内部研发人员占比达到 74%,这意味着,平均每四个腾讯员工中,…...

python爬虫第一节基础概念

爬虫是一种自动化抓取互联网上数据的技术。在网络信息爆炸的今天,爬虫技术已经成为数据获取和信息分析的重要手段。本文将详细介绍爬虫的基础知识和操作,帮助初学者快速入门。 一、爬虫的基本原理 爬虫的基本原理是通过网络请求获取网页源代码&#xf…...

web学习---Vue---笔记(1)

该笔记是记录尚硅谷的Vue学习视频的笔记,视频地址为:学习视频地址 初始Vue Vue组件化的特点 组件化声明式编码虚拟DOMDiff算法,尽量复用DOM节点 H5的组件,是把某一个模块封装,里面写HTML\CSS\JS等,算是一…...

【前端面试题——微信小程序】

目录1.请谈谈wxml与标准的html的异同?2.请谈谈WXSS和CSS的异同?3.请谈谈微信小程序主要目录和文件的作用?4.请谈谈小程序的双向绑定和vue的异同?5.简单描述下微信小程序的相关文件类型?6.微信小程序有哪些传值(传递数据…...

gpt模型训练-gpt3模型详解

训练一个GPT模型需要大量的数据集和计算资源。在这里,我提供一些较为通用的训练步骤以供参考: 获取数据集 首先需要收集一些数据集,数据集建议获取大型的常用文本数据集。常见的例如维基百科、各种在线文章、小说、论文等,数据集…...

vue尚品汇商城项目-day04【27.分页器静态组件(难点)】

文章目录27.分页器静态组件(难点)本人其他相关文章链接27.分页器静态组件(难点) 难点: 考虑点1:为啥需要分页呢? 答案:按需加载 考虑点2:分页器展示,需要哪…...

使用SeaFile搭建私有云盘并公网访问【cpolar内网穿透】

文章目录1. 前言2. SeaFile云盘设置2.1 Owncould的安装环境设置2.2 SeaFile下载安装2.3 SeaFile的配置3. cpolar内网穿透3.1 Cpolar下载安装3.2 Cpolar的注册3.3 Cpolar云端设置3.4 Cpolar本地设置4. 公网访问测试5. 结语1. 前言 现在我们身边的只能设备越来越多,各…...

蓝桥杯第26天(Python)考前挣扎

题型: 1.思维题/杂题:数学公式,分析题意,找规律 2.BFS/DFS:广搜(递归实现),深搜(deque实现) 3.简单数论:模,素数(只需要…...

WuThreat身份安全云-TVD每日漏洞情报-2023-04-04

漏洞名称:RSA NetWitness Platform 内存损坏漏洞 漏洞级别:中危 漏洞编号:CVE-2022-47529,CNNVD-202303-2419 相关涉及:RSA NetWitness Platform 12.2之前版本 漏洞状态:POC 参考链接:https://tvd.wuthreat.com/#/listDetail?TVD_IDTVD-2023-07193 漏洞名称:EyouCms <1.5.…...

【C++】Step by Step的格式化代码风格是这样的吗?

文章目录前言一、依赖二、配置总结前言 本节从0开始讲解如何格式化自己的代码风格&#xff0c;使用vscode插件来完成&#xff0c;本节的所有配置都会在星球同步哦~ 一、依赖 本次使用的是clang-format插件&#xff0c;具体安装比较简单&#xff1a; mac系统&#xff1a; br…...

aspnet030高校学生团体管理系统sqlserver

net030高校学生团体管理系统 . 1.用户基本信息管理模块&#xff1a;录入、修改、删除、查询、统计、打印等功能 2.学生成绩管理模块&#xff1a;录入、修改、删除、查询、统计、打印等功能 3.学生团体信息管理模块&#xff1a;录入、修改、删除、查询、统计、打印等功能 4.教…...

学习HM微博项目第10天

步骤&#xff1a;发微博12-表情键盘06-点击表情 -> 发微博13-表情键盘07-插入表情和封装textView -> 发微博14-表情键盘08-长按表情 -> 发微博15-表情键盘09-最近表情 -> 发微博16-表情键盘10-最近表情完善 发微博12-表情键盘06-点击表情 APP的演示动画&#xff…...

0204强连通性-有向图-数据结构和算法(Java)

文章目录1 概述2 强连通分量2.1 定义2.2 Kosaraju算法2.2.1 算法实现2.2.2算法测试2.2.3 算法理解3 强连通性结语1 概述 定义。如果2个顶点是相互可达的&#xff0c;则称它们为强连通的。如果一幅有向图中的任意两个顶点都是强连通的&#xff0c;则称这幅有向图也是强连通的。 …...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!

5月28日&#xff0c;中天合创屋面分布式光伏发电项目顺利并网发电&#xff0c;该项目位于内蒙古自治区鄂尔多斯市乌审旗&#xff0c;项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站&#xff0c;总装机容量为9.96MWp。 项目投运后&#xff0c;每年可节约标煤3670…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

算法岗面试经验分享-大模型篇

文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer &#xff08;1&#xff09;资源 论文&a…...

人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent

安全大模型训练计划&#xff1a;基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标&#xff1a;为安全大模型创建高质量、去偏、符合伦理的训练数据集&#xff0c;涵盖安全相关任务&#xff08;如有害内容检测、隐私保护、道德推理等&#xff09;。 1.1 数据收集 描…...

Golang——7、包与接口详解

包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...

Unity中的transform.up

2025年6月8日&#xff0c;周日下午 在Unity中&#xff0c;transform.up是Transform组件的一个属性&#xff0c;表示游戏对象在世界空间中的“上”方向&#xff08;Y轴正方向&#xff09;&#xff0c;且会随对象旋转动态变化。以下是关键点解析&#xff1a; 基本定义 transfor…...

QT开发技术【ffmpeg + QAudioOutput】音乐播放器

一、 介绍 使用ffmpeg 4.2.2 在数字化浪潮席卷全球的当下&#xff0c;音视频内容犹如璀璨繁星&#xff0c;点亮了人们的生活与工作。从短视频平台上令人捧腹的搞笑视频&#xff0c;到在线课堂中知识渊博的专家授课&#xff0c;再到影视平台上扣人心弦的高清大片&#xff0c;音…...