【Java多线程】案例(4):定时器

目录

一、定时器是什么?

二、Java标准库中的定时器

三、自己实现定时器

四、标准库中更推荐使用的定时器


一、定时器是什么?

定时器是一种用于在指定时间间隔或特定时间点执行特定任务的工具或设备。在计算机科学中,定时器通常是软件或硬件组件,用于跟踪时间的流逝并在预定的时间触发事件或执行操作

定时器是软件开发中的一个重要组件。类似于一个"闹钟"。达到一个设定的时间之后,就执行某个指定好的代码。

定时器的使用场景:

  • 定时提醒:日历应用程序或提醒应用程序可以使用定时器来触发提醒事件,例如在预定的时间点提醒用户参加会议或生日。

  • 定时任务:定时关闭电视或空调等家用电器,以减少不必要的能源消耗。

  • 游戏开发:在游戏开发中,定时器可以用于实现游戏中的动画效果、计时器功能或限时任务等。

二、Java标准库中的定时器

Java标准库中提供了一个 Timer 类,Timer 类的核心方法为 schedule。

schedule 方法包含两个参数。

  • 第一个参数:指定即将要执行的任务代码,这个参数的类型 TimerTask 是一个抽象类,它实现了 Runnable 接口,就把它当作 Runnable 来使用即可。
  • 第二个参数:指定多长时间之后执行任务(单位为毫秒)。

Timer 类内部包含一个线程,只有这个线程来执行所有的定时任务。这意味着如果有多个任务被安排在同一时间执行,它们将按顺序逐个执行,而不能并行执行。

import java.util.Timer;
import java.util.TimerTask;public class Demo1 {public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("Hello 3000");}}, 3000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("Hello 2000");}}, 2000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("Hello 1000");}}, 1000);}
}

这段代码创建了一个 Timer 对象,并安排了三个定时任务,分别在延迟 3000 毫秒、2000 毫秒和 1000 毫秒后执行。

由于 Timer 内部包含了前台线程,因此进程没有结束。

三、自己实现定时器

需求:

  1. 能够延时执行任务/指定时间执行任务。
  2. 能够管理多个任务。

对于要延时执行的任务,要将其转换成绝对时间(当前的时间戳),这样方便判定后续任务是否要执行,因为如果还是记录延时,需要随着时间的推移不断更新delay,非常麻烦。

  • 我们将任务及任务执行的绝对时间封装成一个类Task,更具体地表示这个任务。
  • 用一个优先级队列保存这些任务,以任务执行的绝对时间靠前为优先级。(不使用 PriorityBlockingQueue,在这个实现中容易死锁)
  • 定时器中需要有线程一直扫描队首元素,看队首是否需要执行。

详细过程见代码:

import java.util.PriorityQueue;//用于描述一个任务的类
class MyTimerTask implements Comparable<MyTimerTask> {//要执行的任务private Runnable runnable;//当前任务实际执行的时间戳private long time;public MyTimerTask(Runnable runnable, long delay) {this.runnable = runnable;//取当前时刻的时间戳+delay(延迟时间),作为当前任务实际执行的时间戳this.time = System.currentTimeMillis() + delay;}public void run() {this.runnable.run();}public long getTime() {return this.time;}@Overridepublic int compareTo(MyTimerTask o) {//试试哪个是升序就可以return (int) (this.time - o.time);//return (int) (o.time - this.time);}
}//定时器
class MyTimer {//用优先级队列存放所有任务,以时间戳升序排序private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();//定时器中存在一个工作线程,不停扫描队首元素,看是否能执行这个任务public MyTimer() {Thread t = new Thread(() -> {while (true) {try {synchronized (this) {//任务队列为空,主动阻塞等待if (queue.isEmpty()) {this.wait();}//看队首元素是否到达要执行的时间MyTimerTask task = queue.peek();long curTime = System.currentTimeMillis();//已经到达(当前时间的时间戳更大),任务执行并出队if (curTime >= task.getTime()) {task.run();queue.poll();} else {//队首还没到达执行时间,则任务队列所有任务都还没到达执行时间//避免重复循环判断,主动阻塞等待(等待的最长时间就是当前的时间间隔)this.wait(task.getTime() - curTime);}}} catch (InterruptedException e) {throw new RuntimeException();}}});t.start();}//安排指定的任务,在指定的时间之后执行public void schedule(Runnable runnable, long delay) {synchronized (this) {MyTimerTask task = new MyTimerTask(runnable, delay);queue.offer(task); //加入到任务队列//有新任务,就唤醒上次阻塞等待的任务//当前新任务的实际执行时间可能更早,此时再判断队首任务是否执行,并更新wait的时间this.notify();}}
}//实现定时器
public class Demo2 {public static void main(String[] args) {MyTimer myTimer = new MyTimer();myTimer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("hello 3000");}}, 3000);myTimer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("hello 2000");}}, 2000);myTimer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("hello 1000");}}, 1000);}
}

执行结果于使用标准库的一致。这里不使用PriorityBlockingQueue的原因是:它只能处理队列为空时候的阻塞,而任务还都未到执行时间时的阻塞,就需要通过额外的锁对象和 wait 来实现。

此时代码就更复杂了,引入了两把锁(额外引入的锁对象和阻塞队列自带的锁),这就容易死锁了,而我们自己控制wait,只需要一把锁,更容易控制代码。

四、标准库中更推荐使用的定时器

由于 Timer 是单线程的,因此不推荐在任务中执行耗时操作或阻塞操作,因为这会影响到其他任务的执行。另外,如果在任务中抛出未捕获的异常,会导致该线程终止,从而影响到其他任务的执行。

Java标准库中还有一种主要的定时器实现,就是使用案例(3)中标准库的线程池 Executors 工厂类里的第四个工厂方法:Executors.newScheduledThreadPool(int corePoolSize)。

这个方法返回ScheduledExecutorService,ScheduledExecutorService 是一种特殊类型的线程池,它具有定时执行任务的功能。它继承自 ExecutorService 接口,同时扩展了定时执行任务的能力。相比于 Timer 类,ScheduledExecutorService 支持更多的灵活性和功能,并且可以更好地处理多个并发任务。

 ScheduledExecutorService 接口的核心方法也是 schedule,与 Timer 不同的是其参数。

  1. 参数 command 是一个 Runnable 对象,表示要执行的任务;
  2. 参数 delay 是延迟时间,以指定的时间单位(TimeUnit)表示;
  3. 参数 unit 则是时间单位,可以是纳秒、毫秒、秒等。
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class Demo3 {public static void main(String[] args) {//使用 Executors.newScheduledThreadPoolScheduledExecutorService threadPool = Executors.newScheduledThreadPool(1);threadPool.schedule(new Runnable() {@Overridepublic void run() {System.out.println("hello 3000");}}, 3000, TimeUnit.MILLISECONDS);threadPool.schedule(new Runnable() {@Overridepublic void run() {System.out.println("hello 2000");}}, 2000, TimeUnit.MILLISECONDS);threadPool.schedule(new Runnable() {@Overridepublic void run() {System.out.println("hello 1000");}}, 1000, TimeUnit.MILLISECONDS);threadPool.shutdown();}
}

 

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

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

相关文章

智过网:注册安全工程师注册有效期与周期解析

在职业领域&#xff0c;各种专业资格认证不仅是对从业者专业能力的认可&#xff0c;也是保障行业安全、规范发展的重要手段。其中&#xff0c;注册安全工程师证书在安全生产领域具有举足轻重的地位。那么&#xff0c;注册安全工程师的注册有效期是多久呢&#xff1f;又是几年一…

Elasticsearch下载安装 以及Reindex(数据迁移)

部署Elasticsearch集群 这里介绍使用的是Elasticsearch 7.6.1的版本&#xff0c;配置两台服务器&#xff0c;一台部署主节点&#xff0c;一台部署两个从节点。 下载地址&#xff1a;https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.16.2-linux-x86_64…

backTrack Mock

1.简而言之&#xff0c;一个集合里求组合就要用startIndex。 2.startIndex本身保证了组合的不同&#xff0c;需要求不同组合就要用startIndex&#xff1b;但从 i 开始还是从 i 1 开始决定了组合元素能不能重复选。&#xff08;39&#xff09; 3.组内既不能重复选&#xff0c…

【Java探索之旅】方法重载 递归

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; Java编程秘籍 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一、方法重载1.1 为什么要有方法重载1.2 方法重载的概念与使用1.3 方法签名 二、递归2…

小程序面试题之性能优化提高11道

1.如何实现上拉加载分页列表的性能优化 我们的功能里面有个滚动到底部加载的功能&#xff0c;优化前我们的做法是这样的&#xff1a; 大部分人面对长列表滚动的时候&#xff0c;一开始的处理方式都是这样的&#xff0c;如果数据不多&#xff0c;只有几页可能不会太暴露问题&…

在QT里使用SQLite数据库

什么是SQLite数据库&#xff1f;SQLite是一种轻量级的数据库管理系统&#xff0c;它不需要一个独立的服务器进程&#xff0c;可以被集成到应用程序中。SQLite是开源的&#xff0c;支持跨平台操作&#xff0c;并且使用非常广泛。在QT里如何使用SQLite数据库呢&#xff1f;废话不…

软考高级架构师:随机函数模型

一、AI 讲解 随机函数模型是理解各种随机过程和算法的一个重要概念&#xff0c;在软件工程、算法设计以及系统分析中有着广泛的应用。简而言之&#xff0c;随机函数模型是一种用于描述具有随机性的系统或过程的数学模型&#xff0c;它能够帮助我们预测和分析在不确定性下的系统…

吴恩达2022机器学习专项课程(一) 5.5 特征缩放1 5.6 特征缩放2

问题预览/关键词 什么是特征缩放&#xff1f;作用是什么&#xff1f;特征尺度和参数w权重的关系是&#xff1f;算法为什么要调节w权重&#xff1f;不进行特征缩放对梯度下降的影响&#xff1f;有特征缩放对梯度下降的影响&#xff1f;实现特征缩放的三种方法是&#xff1f;如何…

JetBrains IntelliJ IDEA 2024.1 发布 - 领先的 Java 和 Kotlin IDE

JetBrains IntelliJ IDEA 2024.1 发布 - 领先的 Java 和 Kotlin IDE 请访问原文链接&#xff1a;JetBrains IntelliJ IDEA 2024.1 (macOS, Linux, Windows) - 领先的 Java 和 Kotlin IDE&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;s…

sqlmap一些常用命令

仅供交流学习使用&#xff0c;请勿用于非法用途 1&#xff09;检测url存在漏洞情况&#xff1a;python sqlmap.py -u "http://192.168.88.128/sqli-labs-master/Less-1/?id1" 2&#xff09;获取所有数据库名称&#xff1a;python sqlmap.py -u "http://192.168…

达梦数据库导入导出工具dmfldr

达梦数据库导入导出工具dmfldr 基础信息 OS版本&#xff1a; Red Hat Enterprise Linux Server release 7.9 (Maipo) DB版本&#xff1a; DM Database Server 64 V8 DB Version: 0x7000c 03134284132-20240115-215128-200811 dmfldr工具介绍 dmfldr&#xff08;DM Fast Loade…

大厂Java笔试题之统计兔子出生问题

题目&#xff1a;有一种兔子&#xff0c;从出生后第3个月起每个月都生一只兔子&#xff0c;小兔子长到第三个月后每个月又生一只兔子。 例子&#xff1a;假设一只兔子第3个月出生&#xff0c;那么它第5个月开始会每个月生一只兔子。 一月的时候有一只兔子&#xff0c;假如兔子…

ip addr和ifconfig区别

ip addr和ifconfig都是用于配置和管理网络接口的工具 1. ifconfig ifconfig是较旧的网络配置工具&#xff0c;属于net-tools套件的一部分。 该命令主要用于配置、显示和控制网络接口的参数&#xff0c;如IP地址、子网掩码、广播地址等。 ifconfig命令的功能相对有限&#xff…

设计模式之责任链讲解

责任链模式适用于需要将请求和处理解耦的场景&#xff0c;同时又需要动态地组织处理逻辑的场景。 通过使用责任链模式&#xff0c;可以实现请求的动态处理、灵活的扩展和简化的代码编写&#xff0c;提高系统的可维护性和可扩展性。 一、责任链入门 以下这是GPT生成的责任链代…

(三)PostgreSQL的pg_ctl命令

PostgreSQL的pg_ctl命令 pg_ctl 是 PostgreSQL 用于控制数据库服务器进程的命令行工具。它提供了启动、停止、重启数据库服务器以及管理其运行状态的手段。pg_ctl 命令尤其适用于从命令行或脚本中管理 PostgreSQL 服务&#xff0c;而不是通过操作系统的服务控制管理器。 基础…

css 太极图案例带来的收获

基础知识 渐变&#xff1a;gradient 在两个或者多个颜色之间显示平稳过度。由浏览器生成。 线性渐变&#xff1a;line-gradient(过渡方向&#xff0c;初始颜色&#xff0c;结束颜色)。注意过渡方向默认从上到下。 1、支持多颜色渐变&#xff0c;多个值&#xff0c;就是从多个…

Springboot整合物联网IOT的MQTT协议

准备工作 (下载EMQX服务端&#xff0c;相关客户端工具) 1. 服务端工具&#xff1a; https://www.emqx.io/downloads?osWindows 2. 客户端工具&#xff1a; https://mqttx.app/zh#download <!--web依赖--><dependency><groupId>org.springframework.boot…

UnityShader学习计划

1.安装ShaderlabVS,vs的语法提示 2. 常规颜色是fixed 3.FrameDebugger调试查看draw的某一帧的全部信息&#xff0c;能看到变量参数的值

架构设计-权限系统之权限系统设计

系统设计权限系统 权限管控可以通俗的理解为权力限制&#xff0c;即不同的人由于拥有不同权力&#xff0c;他所看到的、能使用的可能不一样。对应到一个应用系统&#xff0c;其实就是一个用户可能拥有不同的数据权限&#xff08;看到的&#xff09;和操作权限&#xff08;使用…

前 5 名 iPhone 数据恢复软件评测

如今&#xff0c;我们似乎将整个生活都放在手机和移动设备上。他们用许多照片、备忘录、日历日期等记录了我们的生活&#xff0c;我们总是假设这些信息在我们需要时随时可以访问。但是&#xff0c;有许多情况会导致iPhone上的数据丢失&#xff0c;例如iPhone被盗&#xff0c;损…