【JavaEE】多线程(6) -- 定时器的使用及实现

目录

定时器是什么

标准库中的定时器的使用

实现定时器


定时器是什么

Java中的定时器是一种机制,用于在预定时间执行某个任务。它允许开发人员在指定的时间间隔内重复执行任务,或在指定的延迟之后执行任务。定时器是Java提供的一种方便的工具,用于处理需要定期执行的任务,例如定时任务调度、定时数据备份等。

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

定时器是⼀种实际开发中⾮常常⽤的组件.

⽐如⽹络通信中, 如果对⽅ 500ms 内没有返回数据, 则断开连接尝试重连.

⽐如⼀个 Map, 希望⾥⾯的某个 key 在 3s 之后过期(⾃动删除).

类似于这样的场景就需要⽤到定时器.

标准库中的定时器的使用

标准库中提供了⼀个 Timer 类. Timer 类的核心方法为 schedule .

schedule 包含两个参数: 第一个参数指定即将要执行的任务代码, 第⼆个参数指定多长时间之后执行 (单位为毫秒).

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);System.out.println("hello main");}

 

第一个任务在3000毫秒(3秒)后执行,它的run方法会打印出"hello 3000"。
第二个任务在2000毫秒(2秒)后执行,它的run方法会打印出"hello 2000"。
第三个任务在1000毫秒(1秒)后执行,它的run方法会打印出"hello 1000"。 

实现定时器

定时器的构成

• ⼀个带优先级队列(不要使用 PriorityBlockingQueue, 容易死锁!)

• 队列中的每个元素是⼀个 Task 对象.

• Task 中带有⼀个时间属性, 队首元素就是即将要执行的任务

• 同时有⼀个 worker 线程⼀直扫描队首元素, 看队首元素是否需要执行

创建以下两个类来实现定时器的功能:

1. MyTimer 类提供的核⼼接⼝为 schedule, ⽤于注册⼀个任务, 并指定这个任务多⻓时间后执⾏.

class MyTimer {public void shedule(Runnable runnable, long delay) {}
}

2. Task 类⽤于描述⼀个任务(作为 Timer 的内部类). ⾥⾯包含⼀个 Runnable 对象和⼀个 time(毫秒时 间戳) 这个对象需要放到优先队列 中. 因此需要实现 Comparable 接⼝

class MyTimerTask implements Comparable<MyTimerTask> {public MyTimerTask(Runnable runnable, long delay) {}public void run() {runnable.run();}@Overridepublic int compareTo(MyTimerTask o) {return (int)(this.time - o.time);}
}

Runnable是Java中一个非常重要的接口,它是一个函数式接口,用于定义线程的任务。Runnable接口只包含一个抽象方法run(),该方法是线程的入口点,线程在执行时会调用run()方法中的代码。 

下面是具体的实现过程:

//通过这个类来描述一个任务
class MyTimerTask implements Comparable<MyTimerTask> {//time是一个 ms 级别的时间戳private long time;//实际任务要执行的代码private Runnable runnable;public long getTime() {return time;}public MyTimerTask(Runnable runnable, long delay) {this.runnable = runnable;//计算一下真正要执行任务的绝对时间, 使用绝对时间,方便判定任务是否达到时间this.time = System.currentTimeMillis() + delay;}public void run() {runnable.run();}@Overridepublic int compareTo(MyTimerTask o) {return (int)(this.time - o.time);}
}//通过一个类表示一个任务的进程
class MyTimer {// 负责扫描任务队列, 执行任务的线程private Thread t = null;//任务队列private PriorityQueue<MyTimerTask>queue = new PriorityQueue<>();//锁对象private Object locker = new Object();public void shedule(Runnable runnable, long delay) {synchronized (locker) {MyTimerTask task = new MyTimerTask(runnable,delay);queue.offer(task);//添加新的元素后, 就可以唤醒扫描线程的 wait 了;locker.notify();}}public void cancel() {// 结束t线程// interrupt}//构造方法, 创建扫描线程, 让扫描线程来完成判定和执行public MyTimer() {t = new Thread(() -> {//扫描线程就需要循环反复的扫描队首元素, 然后判定队首元素是不是时间到了//如果时间没到, 啥都不干//如果时间到了, 就执行这个任务并且把这个任务从队列中删除掉while(true) {try {synchronized (locker) {while (queue.isEmpty()) {locker.wait();}MyTimerTask  task = queue.peek();long curTime = System.currentTimeMillis();if(curTime >= task.getTime()) {//当前时间已经达到了任务执行时间, 就可以执行任务了queue.poll();task.run();} else {// 当前时间还没有到, 暂时先不执行// 不能使用sleep, 会错过新的任务, 也无法释放锁locker.wait(task.getTime() - curTime);}}}catch (InterruptedException e) {e.printStackTrace();}}});t.start();}
}

在 MyTimer 的构造方法中, 需要注意加锁的位置:

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

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

相关文章

接口自动化测试问题汇总

本篇文章分享几个接口自动化用例编写过程遇到的问题总结&#xff0c;希望能对初次探索接口自动化测试的小伙伴们解决问题上提供一小部分思路。 sql语句内容出现错误 空格&#xff1a;由于有些字段判断是变量&#xff0c;需要将sql拼接起来&#xff0c;但是在拼接字符串时没有…

低信噪比环境下的语音端点检测

端点检测技术 是 语音信号处理 的关键技术之一为提高低信噪比环境下端点检测的准确率和稳健性&#xff0c;提出了一种非平稳噪声抑制和调制域谱减结合功率 归一化 倒谱距离的端点检测算法 1 端点检测 1-1 定义 定义&#xff1a;在 存在背景噪声 的情况下检测出 语音的起始点和…

一篇文章带你轻松入门Python

Python基础 1. Hello World! Python命令行 假设你已经安装好了Python, 那么在命令提示符输入: python 将直接进入python。然后在命令行提示符>>>后面输入: >>>print(Hello World!) 可以看到&#xff0c;随后在屏幕上输出: print是一个常用函数&#xf…

TransNeXt:稳健的注视感知ViT学习笔记

论文地址&#xff1a;https://arxiv.org/pdf/2311.17132.pdf 代码地址&#xff1a; GitHub - DaiShiResearch/TransNeXt: Code release for TransNeXt model 可以直接在ImageNet上训练的分类代码&#xff1a;GitHub - athrunsunny/TransNext-classify 代码中读取数据的部分修改…

使用cmake配置matplotlibcpp生成VS项目

https://gitee.com/feboreigns/matplotlibcpp 这篇文章需要一些cmake基础&#xff0c;python基础&#xff0c;visualstudio基础 准备环境 注意如果在VS平台使用必须要手动下载python&#xff0c;不能使用conda里面的&#xff0c;比如3.8版本&#xff0c;因为conda里面没有py…

k8s的二进制部署和网络类型

k8s的二进制部署 master01&#xff1a;192.168.233.10 kube-apiserver kube-controller-manager kube-scheduler etcd master02&#xff1a;192.168.233.20 kube-apiserver kube-controller-manager kube-scheduler node01&#xff1a;192.168.233.30 kubelet kube-proxy etc…

查看pytorch模型的权重是否导入成功

对于pytorch的模型的对象&#xff0c;可以查看其state_dict属性&#xff0c;可以查看load ckpt前后的权重值是否发生变化&#xff0c;查看权重是否加载成功

算法导论复习纲要

函数 1. 上界下界&#xff0c;紧确界的定义 2. 求解递推式&#xff0c;代入法&#xff0c;递归树法&#xff0c;主方法 分治算法 动态规划 1. 切割钢条&#xff1a;递归方法&#xff0c;动态的自上而下&#xff0c; 2. 矩阵乘法&#xff1a;最优子结构性的证明&#xff0c…

Matlab:非线性规划

1、语法&#xff1a; xfmincon(fun,x0,A,b) xfmincon(fun,x0,A,b,Aeq,beq) xfmincon(fun,x0,A,b,Aeq,beq,lb,ub) xfmincon(fun,x0,A,b,Aeq,beq,lb,ub,nonlcon) xfmincon(fun,x0,A,b,Aeq,beq,lb,ub,nonlcon,options) xfmincon(problem) [x,fval]fmincon(___) [x,fval,exitflag,…

《长江丛刊》期刊投稿发表

《长江丛刊》杂志是由国家新闻出版总署批准的正规期刊&#xff0c;是一本文学类综合性刊物&#xff0c;包括文学评论以及文学作品的发表。是广大专家、学者、教师、学子发表论文、交流信息的重要平台&#xff0c;符合评职称要求&#xff08;单位有特殊要求除外&#xff09;。 …

数据结构学习 Leetcode494 目标和

关键词&#xff1a;动态规划 01背包 dfs回溯 一个套路&#xff1a; 01背包&#xff1a;空间优化之后dp【target1】&#xff0c;遍历的时候要逆序遍历完全背包&#xff1a;空间优化之后dp【target1】&#xff0c;遍历的时候要正序遍历 题目&#xff1a; 解法一&#xff1a; …

MySQL数据库多版本并发控制(MVCC)

在数据库中&#xff0c;并发控制是确保多个事务能够同时执行&#xff0c;而不会导致数据不一致或冲突的关键机制。多版本并发控制(MVCC)是一种流行的并发控制方法&#xff0c;它可以允许多个事务同时读取同一数据项的不同版本&#xff0c;而不会相互阻塞。本文将讨论MVCC的原理…