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

12. 多线程(9) --- 案例:定时器

文章目录

  • 前言
  • 1. 什么是定时器
  • 2. 标准库中的定时器
  • 3. 自己实现定时器


前言

我们在上个博客中学习到了 阻塞队列的使用和模拟实现,这次我们学习 定时器


1. 什么是定时器

定时器是软件开发中的一个重要组件。类似于一个 “闹钟”。达到一个设定的时间之后,就执行某个指定好的代码。
在这里插入图片描述

定时器是一种实际开发中非常常用的组件。
比如在网络通信中,如果对方 500ms 内没有返回数据,则断开连接尝试重连
比如一个 Map,希望里面的某个 key 在 3s 之后过期 (自动删除)
类似于这样的场景就需要用到定时器


2. 标准库中的定时器

  • 标准库中提供了一个Timer类,Timer类的核心方法为 schedule.
  • schedule 包含两个参数,第一个参数指定即将要执行的任务代码, 第二个参数指定多长时间之后执行 (单位为毫秒)。

有两种实现方法:
1.

public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("Hello world");}},300);}
public static void main(String[] args) {TimerTask timerTask = new TimerTask() {@Overridepublic void run() {System.out.println("Hello World");}};Timer timer = new Timer();timer.schedule(timerTask,3000);}

这两种方法都行。
一般来说,是对 Runnable 进行重写,在定时器这 TimeTask 用 Runnable 封账了一下。
在这里插入图片描述

我们写一下下面的代码,观察效果。

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);}

我们发现这个代码和上面的代码在运行完之后,程序是不会停下来的,这是因为:
当 打印完 “Hello 3000” 在 3000ms 打印完之后,并不意味着 Timer 结束.
实际上是,Timer 线程可能还在等待其他任务 (如果有的话) 或者处于空闲状态等待新的任务。由于并没有什么其他任务提交给 Timer,所以当Timer中所有的任务执行完毕之后,就会等待,也可以显式调用 timer.cancel() 提前结束。

可以使用下面的代码,来观察效果。

public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("Hello 3000");timer.cancel();}},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);}

3. 自己实现定时器

根据上面的解释,其实可以发现构建定时器是要通过 阻塞队列,直观点就是 带有 优先级阻塞队列
以下思考点:

  1. 一个任务一个类,这样来表示吗?

这个就是返回到最早学习Java语法,1,2,3,4, 这么多的数,需要用a,b,c不同的变量来保存吗,可以直接使用数组来存储。因此,我们可以使用集合类把每个任务存储在一块。

  1. 用什么集合类,使用 ArrayList 可以吗?

任务是有执行时机的,我们把任务存储在ArrayList中,那就需要遍历每个任务的时机,任务早的先执行。

  1. 那么如何可以通过在创建集合的时候,就可以按照执行的先后顺序,把任务存储到集合中呢?

我们可以使用 来执行。

我们首先创建一个类,既包含任务本身,又包含时间。

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

在 MyTimer 中,我们在初始化过程中,使用一个线程负责执行队列中的任务,并且写 schedule方法来向优先级队列添加任务。

class MyTimer{// 优先级队列private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();private Object locker = new Object();public MyTimer(){Thread t = new Thread(()->{while (true){if (queue.isEmpty()){continue;}MyTimerTask task = queue.peek();if (System.currentTimeMillis() < task.getTime()){// 如果当前时间比 任务的时间少,那么说明还没有到达指定的时间continue;}else {task.run();queue.poll();}}});t.start();}public void schedule(Runnable task,long delay){MyTimerTask myTimerTask = new MyTimerTask(task,delay+System.currentTimeMillis());queue.offer(myTimerTask);}
}

这样子写是一定不行的,我们对上面的代码需要进行修改。

  1. 在添加任务的时候,需要做到有序,需要上锁来处理。
 public void schedule(Runnable task,long delay){synchronized (locker){MyTimerTask myTimerTask = new MyTimerTask(task, delay + System.currentTimeMillis());queue.offer(myTimerTask);locker.notify();}}
  1. 上面有两处 continue ,这个方法会死等,一直消耗 cpu 的资源,我们需要根据运行时间来进行分配。
 Thread t = new Thread(()->{try {while (true){if (queue.isEmpty()){locker.wait();}MyTimerTask task = queue.peek();if (System.currentTimeMillis() < task.getTime()){// 如果当前时间比 任务的时间少,那么说明还没有到达指定的时间locker.wait(task.getTime()-System.currentTimeMillis());continue;}else {task.run();queue.poll();}}}catch (InterruptedException e) {throw new RuntimeException(e);}});

总的代码:

import java.nio.FloatBuffer;
import java.util.PriorityQueue;/*** @Author: XXHH* @CreateTime: 2025-05-02*/
class MyTimerTask implements Comparable<MyTimerTask>{private long time;private Runnable task;public MyTimerTask(Runnable task,long time) {this.time = time;this.task = task;}@Overridepublic int compareTo(MyTimerTask o) {return (int)(this.getTime()-o.getTime());}public void run(){task.run();}public long getTime(){return this.time;}
}
class MyTimer{// 优先级队列private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();private Object locker = new Object();public MyTimer(){Thread t = new Thread(()->{try {while (true){if (queue.isEmpty()){locker.wait();}MyTimerTask task = queue.peek();if (System.currentTimeMillis() < task.getTime()){// 如果当前时间比 任务的时间少,那么说明还没有到达指定的时间locker.wait(task.getTime()-System.currentTimeMillis());continue;}else {task.run();queue.poll();}}}catch (InterruptedException e) {throw new RuntimeException(e);}});t.start();}public void schedule(Runnable task,long delay){synchronized (locker){MyTimerTask myTimerTask = new MyTimerTask(task, delay + System.currentTimeMillis());queue.offer(myTimerTask);locker.notify();}}
}
public class Demo41 {public static void main(String[] args) {MyTimer timer = new MyTimer();timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("hello 3000");}}, 3000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("hello 2000");}}, 2000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("hello 1000");}}, 1000);}
}

在这里插入图片描述


下一个我们讲解 线程池,我们不见不散!

相关文章:

  • DeepWiki 是什么,怎么使用
  • SIFT算法详细原理与应用
  • 力扣第448场周赛
  • Android学习总结之GetX库篇(场景运用)
  • 蓝桥杯单片机备战笔记
  • Easy云盘总结篇-回收站
  • 常见的位置编码 Positional Encoding
  • 1. std::result_of是什么?为什么它出现?
  • c++存储持续性和链接性
  • Netty的内存池机制怎样设计的?
  • Webug4.0靶场通关笔记15- 第19关文件上传(畸形文件)
  • 服务器端的准备工作
  • 求解器介绍之gurobi
  • Linux电源管理(6)_Generic PM之挂起功能
  • 【自然语言处理与大模型】LlamaIndex的数据连接器和对话引擎
  • 二、Python变量基础(2)
  • 30天开发操作系统 第27天 -- LDT与库
  • 工业主义与民主的兴衰:历史逻辑与未来危机
  • uniswap v4 合约解析1 pool初始化
  • VTK 数据结构和算法类介绍
  • 4月份全球制造业PMI继续下降,经济下行压力有所加大
  • 涉“子宫肌瘤”论文现55例男性对照观察患者?山大齐鲁医院:正在调查
  • 申活观察|演出场次破纪录、入境游导游档期忙,上海文旅商“热力”拉满
  • 晋城一男子实名举报村支书打伤其67岁父亲,镇政府:案件正在侦办中
  • 美国鞋类巨头请求白宫豁免关税,称已构成“生存威胁”
  • 叙利亚多地遭以色列空袭