【多线程学习】深入探究定时器的重点和应用场景

ops/2024/9/20 1:20:33/ 标签: 学习, java, java-ee

˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱
ʕ̯•͡˔•̯᷅ʔ大家好,我是xiaoxie.希望你看完之后,有不足之处请多多谅解,让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客
本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如需转载还请通知˶⍤⃝˶​
个人主页:xiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客

系列专栏:xiaoxie的JAVAEE学习系列专栏——CSDN博客●'ᴗ'σσணღ
我的目标:"团团等我💪( ◡̀_◡́ ҂)" 

( ⸝⸝⸝›ᴥ‹⸝⸝⸝ )欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​+关注(互三必回)!

目录

 一.定时器

1.什么是定时器

2.定时器的应用场景

3.如何在Java中创建定时器

 1.使用 Java.util.Timer 类

2.使用ScheduledExecutorService接口

3.两个方法的注意事项

 4.定时器的底层执行顺序

java%E4%BB%A3%E7%A0%81%E8%87%AA%E5%AE%9A%E4%B9%89%E4%B8%80%E4%B8%AA%E5%AE%9A%E6%97%B6%E5%99%A8-toc" style="margin-left:40px;">5.使用java代码自定义一个定时器

1.代码解析

2.注意事项


 

 一.定时器

1.什么是定时器

在Java中定时器通常指的是一种能够按照预定的时间间隔执行任务的机制,简单来说,定时器就相当于一个"闹钟",指定一个(Runnable) 任务,以及指定一个时间,该任务因为定时器的缘故在线程中并不会立马就执行,而是到达某个指定的时间后,才执行

2.定时器的应用场景

Java中的定时器(Timer)在许多应用场景中都非常有用,下面是几个常见的例子:

  1. 定时任务执行

    • 定期清理缓存:应用程序可以设置定时器来定时清理不再需要的缓存数据,避免内存占用过高。
    • 数据备份:数据库系统可以使用定时器在每天的固定时间执行数据备份操作。
    • 日志滚动:系统可以按计划滚动日志文件,比如每24小时创建一个新的日志文件。
  2. 周期性更新

    • 动态刷新数据:网页应用程序中,定时器可用于刷新显示的内容,例如实时股票价格、天气预报等。
    • API轮询:在等待异步响应或资源的状态变化时,可以设置定时器定期发起API请求查询最新状态。
  3. 邮件通知

    • 提醒服务:例如,用户注册后发送确认邮件,或者每日/每周发送新闻简报、报告等。
    • 系统监控告警:当系统指标超过阈值时,定时器可以用来检查系统状态并在必要时发送警告邮件。
  4. 会话管理

    • 用户会话过期处理:在Web应用中,定时器可以用来检查并清理长时间未活动的用户会话。
  5. 心跳检测

    • 网络连接健康检查:客户端和服务端之间可以利用定时器发送心跳包来维持长连接,及时发现连接异常。
  6. 延迟执行

    • 延迟发送消息:例如在消息队列中,可以设置一条消息在一段时间后才投递给消费者。
  7. 调度任务

    • 批处理作业:在企业级应用中,定时器可以调度批处理作业在非高峰时段执行,减轻服务器负载。
  8. 社交媒体更新

    • 社交媒体平台可以使用定时器定期检查用户的动态更新,例如好友状态更改或新消息提醒。

总之,任何需要按照预设时间间隔或特定时间点执行任务的地方都可以考虑使用定时器机制。

3.如何在Java中创建定时器

在Java中创建定时器通常有两种方式,使用java.util.Timer类或ScheduledExecutorService接口。以下是两种方式的基本示例:

 1.使用 Java.util.Timer 类

java">import java.util.Timer;
import java.util.TimerTask;public class TimerExample {public static void main(String[] args) {Timer timer = new Timer();// 任务类TimerTask task = new TimerTask() {@Overridepublic void run() {System.out.println("任务执行了");}};// 安排任务在1秒后执行,然后每隔1秒执行一次timer.scheduleAtFixedRate(task, 1000, 1000);// 记得在程序结束时取消定时器,以避免潜在的内存泄漏// timer.cancel();}
}

2.使用ScheduledExecutorService接口

java">import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class ScheduledExecutorExample {public static void main(String[] args) {ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();Runnable task = new Runnable() {@Overridepublic void run() {System.out.println("Scheduled任务执行了");}};// 安排任务在1秒后执行,然后每隔1秒执行一次executor.scheduleAtFixedRate(task, 1, 1, TimeUnit.SECONDS);// 记得在程序结束时关闭executor,以释放资源// executor.shutdown();}
}

3.两个方法的注意事项

  • 使用Timer时,所有的任务都由同一个线程顺序执行,如果某个任务执行时间过长,可能会影响其他任务的执行时间。
  • 使用ScheduledExecutorService时,可以控制任务执行的线程池,这使得它更适合执行可能阻塞的任务,或者需要并行执行的任务。
  • 在应用程序不再需要定时器时,应该调用timer.cancel()executor.shutdown()来停止定时器,避免线程泄露。
  • 如果希望立即停止所有正在执行的任务,并不再接受新的任务,可以使用executor.shutdownNow()方法
  • 简单来说,使用 Java.util.Timer 类 就是在后台创建一个 线程或多个线程 默认情况下,Timer只创建一个线程来顺序执行所有任务。这意味着如果一个任务的执行时间过长,它将阻塞队列中的其他任务。线程池允许更好地控制任务的执行,例如设置最大线程数、线程存活时间、工作队列等。使用线程池可以有效地管理资源,提高程序的响应速度和效率。

 4.定时器的底层执行顺序

Java java.util.Timer 类作为基础定时器实现,其底层执行顺序遵循以下逻辑:

  1. 任务调度

    • 当用户创建 Timer 实例并使用 schedule() 或 scheduleAtFixedRate() 方法来调度 TimerTask 时,这些任务会被添加到 Timer 的内部 TaskQueue 队列中。这个队列是按照任务的执行时间进行排序的,即按照每个任务下一次应该被执行的时间。
  2. 任务轮询

    • Timer 创建一个名为 TimerThread 的后台线程,这个线程会在启动后进入一个无限循环
    • 在循环中,TimerThread 不断地检查 TaskQueue 中是否有已到达执行时间的任务。
    • 如果发现一个或多个任务已经到期,则按照队列的顺序取出任务并执行它的 run() 方法。
  3. 执行顺序保证

    • 根据时间优先级排序,最先到期的任务会先被执行。
    • 对于固定延迟调度(schedule()),任务的实际执行时间可能受前一个任务执行时间和线程调度的影响,不一定严格按计划时间执行。
    • 对于固定速率调度(scheduleAtFixedRate()),即使前一个任务执行超时导致延迟,系统仍尝试按照固定的频率执行任务,即从上一次任务开始执行的时间点算起,每隔一定时间执行一次。
  4. 并发问题

    • 由于 Timer 是基于单线程模型的,如果一个任务执行时间过长或者抛出未捕获的异常,那么后续的任务可能会延迟执行,甚至无法执行。这是因为单个 Timer 的所有任务都在同一个 TimerThread 上执行,这意味着任务之间没有并发执行能力,且一旦遇到阻塞或异常情况,整个定时器的工作将会受到影响。

因此,虽然理论上 Timer 底层执行顺序是严格按照任务设定的时间顺序进行的,但在实际应用中,尤其是考虑到多任务环境下的线程调度不确定性以及单线程模型的局限性,应当谨慎使用 java.util.Timer 类以避免潜在的并发问题。对于更复杂或对定时任务执行顺序和可靠性要求较高的场景,推荐使用诸如 ScheduledExecutorService 这样的并发框架提供的服务。

java%E4%BB%A3%E7%A0%81%E8%87%AA%E5%AE%9A%E4%B9%89%E4%B8%80%E4%B8%AA%E5%AE%9A%E6%97%B6%E5%99%A8">5.使用java代码自定义一个定时器

通过了解定时器的底层执行顺序,为了帮助我们更好的理解定时器,我们就可以自己定义一个定时器.增加理解

java">// 定义一个实现了Comparable接口的定时任务类,用于存储Runnable任务及其执行时间
class MyTimerTask implements Comparable<MyTimerTask> {private Runnable runnable; // Runnable对象,代表具体要执行的任务private long time; // 记录该任务应该被执行的具体时间(系统当前时间+延迟时间)// 构造方法,传入一个Runnable任务和延迟执行的时间public MyTimerTask(Runnable runnable, long delay) {this.runnable = runnable;this.time = System.currentTimeMillis() + delay;}// 获取任务的执行时间public long getTime() {return time;}// 执行任务public void run() {runnable.run();}// 重写compareTo方法,用于比较两个任务的执行时间,以便放入PriorityQueue按时间顺序排序@Overridepublic int compareTo(MyTimerTask o) {return (int) (this.getTime() - o.getTime());}
}// 定时器类,负责调度任务
class MyTimer {private PriorityQueue<MyTimerTask> q = new PriorityQueue<>(); // 存储定时任务的优先级队列private Object locker = new Object(); // 同步锁对象,用于线程同步// 定时器构造方法,启动一个后台线程执行定时任务public MyTimer() {Thread t = new Thread(() -> {try {// 无限循环,直到程序结束while (true) {synchronized (locker) {// 如果队列为空,则等待新的任务加入if (q.size() == 0) {locker.wait();}// 获取队列中下一个即将执行的任务MyTimerTask myTimerTask = q.peek();long nowTime = System.currentTimeMillis();// 检查当前时间是否大于等于任务执行时间if (nowTime >= myTimerTask.getTime()) {// 执行任务并从队列中移除已完成的任务myTimerTask.run();q.poll();} else {// 如果任务未到执行时间,线程等待剩余时间locker.wait(myTimerTask.getTime() - nowTime);}}}} catch (InterruptedException e) {throw new RuntimeException(e);}});t.start();}// 调度方法,将一个任务及其延迟执行时间添加到定时器中public void schedule(Runnable runnable, long delay) {synchronized (locker) {MyTimerTask task = new MyTimerTask(runnable, delay); // 创建一个新的定时任务q.offer(task); // 将任务添加到优先级队列中locker.notify(); // 唤醒等待的任务调度线程}}
}// 主函数,演示如何使用MyTimer类调度任务
public class Demo18 {public static void main(String[] args) {MyTimer myTimer = new MyTimer();// 调度三个任务,分别延迟1000ms、2000ms和3000ms执行myTimer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("hello 1000");}}, 1000);myTimer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("hello 2000");}}, 2000);myTimer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("hello 3000");}}, 3000);}
}

1.代码解析

  1. MyTimerTask 类是一个自定义的定时任务类,它封装了一个 Runnable 对象(即任务实体)和一个表示任务执行时间的 long 变量。由于实现了 Comparable 接口,任务可以根据其执行时间在优先级队列中进行排序。

  2. MyTimer 类是定时器类,维护了一个优先级队列 (PriorityQueue) 来存放 MyTimerTask 对象。在构造方法中启动了一个后台线程,该线程会不断地检查并执行优先级队列中最接近当前时间的任务。

  3. 在后台线程的无限循环中,首先检查优先级队列是否为空。如果为空,则调用 wait() 方法让当前线程进入阻塞状态,直到有新的任务被添加到队列中。然后取出队列顶端的任务,判断其执行时间是否已到。如果已到则执行任务并移除,否则继续等待剩余时间

  4. schedule 方法允许外部向定时器中添加一个 Runnable 任务和对应的延迟执行时间。将任务封装成 MyTimerTask 并添加至优先级队列中,随后调用 locker.notify() 唤醒等待的任务调度线程,使其重新检查队列并执行任务。

2.注意事项

1.该代码只是帮助更好的理解定时器的执行顺序以及一些执行原理,如果我们实际开发的时候需要用到定时器的话,我们最好的使用 java.util.Timer类或ScheduledExecutorService接口来定义定时器,

感谢你的阅读,祝你一天愉快


http://www.ppmy.cn/ops/7802.html

相关文章

【技术】Spring Boot 将 Word 转换为 PDF 2.0 版本

之前写过一篇 Spring Boot 将 Word 转换为 PDF 的文章&#xff0c;但是有评论说导入依赖有问题&#xff0c;还存在依赖冲突的问题。索性再来一个完整版的代码&#xff0c;之前的完整版代码找不到了&#xff0c;又重新整理了一下&#xff0c;依赖导入和之前不太一样&#xff0c;…

Linux动态追踪——eBPF

目录 摘要 1 什么是 eBPF 2 eBPF 支持的功能 3 BCC 4 编写脚本 5 总结 6 附 摘要 ftrace 和 perf 与 ebpf 同为 linux 内核提供的动态追踪工具&#xff0c;其中 ftrace 侧重于事件跟踪和内核行为的实时分析&#xff0c;perf 更侧重于性能分析和事件统计&#xff0c;与…

达梦数据库的监听进程

达梦数据库&#xff08;DMDB&#xff09;也同样有监听进程&#xff0c;其主要职责是监听来自客户端的连接请求。这里的监听进程指的是达梦数据库服务启动后&#xff0c;在操作系统级别监听特定TCP/IP端口&#xff08;默认情况下是5236&#xff0c;但这可以配置&#xff09;的进…

Spring Security之Session管理

前言 在聊认证过滤器的时候&#xff0c;我们埋了个坑&#xff1a;Session管理。实际上&#xff0c;事情从这里开始&#xff0c;就变得复杂了。提前跟大家交个底&#xff1a;后续我们将涉及多个需要协同才能完成的功能。 什么是Session 想要管理session&#xff0c;就必须搞清…

Web前端框架/库/工具

前言 前端从步枪&#xff08;原生js&#xff09;到了半自动武器&#xff08;jQuery&#xff09;并进化为全自动武器&#xff08;三大框架&#xff08;angular&#xff0c;react&#xff0c;vue及其生态链&#xff09;&#xff09;。 常说工欲善其事必先利其器。对于那些想要提…

TPCC MySQL

目录 1. tpcc-mysql下载 2. tpcc-mysql安装 3. 初始化tpcc-mysql 4. 使用tpcc-mysql测试 5. 结果解释 1. tpcc-mysql下载 https://github.com/Percona-Lab/tpcc-mysql 2. tpcc-mysql安装 [rootlocalhost soft]# unzip tpcc-mysql-master.zip [rootlocalhost soft]# cd t…

代码随想录训练营24day-贪心算法2

一、122 买卖股票最佳时机 题目介绍限制条件&#xff0c;必须卖了再买&#xff0c;而且当前交易一只股票。一开始想法是去遍历&#xff0c;找到每个区间段间的差值&#xff0c;然后再相加。看了解答&#xff0c;其实每一天的利润&#xff0c;都是可以用差值表示出来&#xff0…

Spring - 1 ( 8000 字 Spring 入门级教程 )

一&#xff1a;SpringBoot 快速上手 环境准备 ⾃检Idea版本: 社区版: 2021.1 -2022.1.4专业版: ⽆要求 如果个⼈电脑安装的idea不在这个范围, 需要卸载重新安装.&#xff08;⼀定要删除注册表&#xff09; Maven Maven是⼀个项⽬管理⼯具。基于POM(Project Object Model,…

centos 7.9 安装 ftp 传输文件

ftp server 端 sudo yum install vsftpd ftp其中 vsftpd 为 ftp server 端&#xff0c;ftp 包含 ftp 这个客户端命令。 # sudo rpm -ql vsftpd/etc/logrotate.d/vsftpd /etc/pam.d/vsftpd /etc/vsftpd /etc/vsftpd/ftpusers /etc/vsftpd/user_list /etc/vsftpd/vsftpd.conf /…

耀斑层-如何在Unity中实现耀斑亮光效果

在Unity中实现耀斑亮光效果可以通过以下步骤来实现&#xff1a; 创建一个空物体作为光源&#xff1a;在场景中创建一个空物体&#xff0c;并将其放置在需要发出耀斑亮光效果的位置上。 添加光源组件&#xff1a;选中空物体&#xff0c;在Inspector面板中点击"Add Compone…

C# 下记录(Record)详解

在C# 9.0中&#xff0c;引入了一个新的关键字&#xff1a;record。record关键字用于定义记录类型&#xff0c;这是一种不可变的数据结构&#xff0c;用于表示具有明确字段名称和类型的数据集。本文将详细介绍C#中record类型的使用和特点&#xff0c;以及如何通过记录记录器&…

使用easyexcel将csv转为excel

一.背景 供应商系统下载的csv文件不支持域控&#xff08;主要是第三方wps服务不能对csv文件加密&#xff0c;但是可以对office系列产品进行权限访问的加密控制&#xff09;。因此思路就改为现将csv文件转为excel文件&#xff0c;然后对excel文件进行加域控制。本文主要介绍如何…

数据结构--双向链表

在讲双向链表之前&#xff0c;我们先了解一下链表的分类&#xff1a; 链表的结构⾮常多样&#xff0c;主要分为带头与不带头、单向与双向、循环与不循环。三个种类可以任意搭配&#xff0c;所以总共可以形成八种链表&#xff0c;但是最常用的是单向不带头不循环链表和双向带头循…

html、css、QQ音乐移动端静态页面,资源免费分享,可作为参考,提供InsCode在线运行演示

CSDN将我上传的免费资源私自变成VIP专享资源&#xff0c;且作为作者的我不可修改为免费资源&#xff0c;不可删除&#xff0c;寻找客服无果&#xff0c;很愤怒&#xff0c;&#xff08;我发布免费资源就是希望大家能免费一起用、一起学习&#xff09;&#xff0c;接下来继续寻找…

代码托管基础操作

在待上传代码文件夹中右键&#xff0c;打开Git Bash Here依次输入以下命令&#xff1a; git init(在本地初始化一个代码仓库&#xff0c;具体表现为会在你的文件夹里出现一个隐藏的.git文件夹) git add .&#xff08;先把代码放到本地的一个缓冲区&#xff09;添加当前目录下的…

命理八字之答案之书前端uniapp效果实现

#uniapp# #答案之书# 不讲废话&#xff0c;先上截图 <div class"padding"><div class"flex align-center justify-center" style"padding-top:100px;"><div class"radarContainer"><div id"radarBox"…

初识ansible变量及实例配置

目录 1、为什么要使用变量 2、变量分类 3、 变量详解 3.1 vars,vars_files , group_vars 3.1 .1 vars 剧本中定义变量 3.1.2 vars_file 将变量存放到一个文件中&#xff0c;并在剧本中引用 3.1.3 group_vars 创建一个变量文件给某个组使用 实例1-根据不同的主机…

[ LeetCode ] 题刷刷(Python)-第35题:搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 nums 为 无重复元素 的 升序 排列数组 请必须使用时间复杂度为 O(log n) 的算法。 示例 示例 1: 输入: …

Django老项目升级到新版本

手上有个 Django 老项目&#xff0c;一直跑得好好的&#xff0c;好几年没动过了&#xff0c;维护费收得正爽&#xff0c;没想到客户来了个新的运营人员&#xff0c;丢了个改动需求过来。我一看也没啥大改&#xff0c;就答应了。大意了。 问题 刚开始改&#xff0c;我这种老鸟…

MongoDB聚合运算符:$sampleRate

MongoDB聚合运算符&#xff1a;$sampleRate 文章目录 MongoDB聚合运算符&#xff1a;$sampleRate语法使用举例 $sampleRate聚合运算符用$match&#xff0c;按照指定的抽样比例&#xff0c;从输入的文档中随机选择相应的文档。 语法 { $sampleRate: <non-negative float>…