【JavaEE】【多线程】定时器

news/2024/10/29 23:14:23/

目录

  • 一、定时器简介
    • 1.1 Timer类
    • 1.2 使用案例
  • 二、实现简易定时器
    • 2.1 MyTimerTask类
    • 2.2 实现schedule方法
    • 2.3 构造方法
    • 2.4 总代码
    • 2.5 测试

一、定时器简介

定时器:就相当于一个闹钟,当我们定的时间到了,那么就执行一些逻辑。

1.1 Timer类

Java的标准库中提供了在java.util包下的Timer类作为定时器。
有如下的构造方法:
四种:

  1. timer() 无参构造;
  2. timer(boolean isDaemon) 创建的线程都是后台线程;
  3. timer(String name) 给定时器中创建的线程名字;
  4. timer(String name, boolean isDaemon) 创建的线程都是后台线程,也给定时器中创建的线程名字。

在Timer类中的核心方法是schedule方法。

  1. schedule(Timer task, Date time) 到达time时刻后执行task任务;
  2. schedule(Timer task, Date firstTime, long period) 到达time时刻后重复执行task任务,每次相隔period时间;
  3. schedule(Timer task, long delay) 在delay时间后执行task任务;
  4. schedule(Timer task, long delay, long period) 在delay时间后重复执行task任务,每次相隔period时间;
  5. scheduleAtFixedRate(Timer task, Date firstTime, long period) 到达time时刻后重复执行task任务,每次执行period时间;
  6. scheduleAtFixedRate(Timer task, long delay, long period) 在delay时间后重复执行task任务,每次执行period时间;

schedule的第一个参数是TimerTask类,这是一个实现了Runnable接口的抽象类。

1.2 使用案例

我们使用schedule方法来打印不同时间执行不同内容。

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

结果如下:会按照等待时间由小到大打印内容,并且执行完之后并不会结束,这是因为这些线程是前台线程。

二、实现简易定时器

自己实现的定时器主要要考虑下面几个内容:

  1. 设计一个类表示任务,对应TimerTask类;
  2. 使用优先级队列来组织多个任务,每次根节点都是等待时间最短的任务;
  3. 实现schedule方法,把任务添加到队列中;
  4. 额外创建一个线程,负责执行队列中的任务,根据时间来执行(即判断是否到了该执行的时间了)。

2.1 MyTimerTask类

这个类中需要:

  • 将要执行的任务,和任务要执行的时刻记录下来,
  • 并且这个任务还要有通过时刻比较得方法(即实现Comparator接口,重写CompareTo方法),便于后面存储进优先级队列。

代码:

java">class MyTimerTask implements Comparable<MyTimerTask>{//记录任务private Runnable task = null;//记录执行任务的时刻private long current = 0;public MyTimerTask(Runnable task, long current) {this.task = task;this.current = current;}public Runnable getTask() {return task;}public long getCurrent() {return current;}@Overridepublic int compareTo(MyTimerTask o) {return (int)(this.current - o.current);}
}

2.2 实现schedule方法

我们实现schedule方法:

  • 只需要将当前的任务传入队列中即可。
  • 将参数Runnable的任务和时刻用来创建MyTimerTask类,在入队即可。
  • 我们还要使用notify为后面的线程中因为队列为空调用wait进入阻塞状态提供唤醒。

代码:

java">private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();public void schedule(Runnable task, long delay) {synchronized (this) {MyTimerTask myTimerTask = new MyTimerTask(task, System.currentTimeMillis() + delay);queue.offer(myTimerTask);this.notify();}}

2.3 构造方法

在构造方法中额外创建一个线程,负责执行队列中的任务,根据时间来执行(即判断是否到了该执行的时间了)。

  • 我们在最外层使用一层死循环来不断去读取队列中的任务。
  • 如果队列空了,那么我们就出这次循环,但是如果使用continue的话,还是会在循环的去判断直到队列不为空为止。这样的消耗很高,我们可以使用wait等待schedule方法入队列后;来唤醒这个线程。
  • 如果没有到达执行时间,我们也要出这次循环,但是使用continue也会导致在从现在这个时刻到执行时刻之间一直进行无意义的执行上面的代码,消耗很高,我们这里直接使用带参数的wait方法等待还需要的时间即可。
  • 到达执行时间直接执行任务并出队列即可。
  • 最后不要忘记启动这个线程。

代码:

java">public MyTimer() {Thread thread = new Thread(()-> {try {while(true) {  //循环拿任务,直到任务队列为空synchronized (this) {while (queue.isEmpty()) {  //任务队列为空this.wait();}MyTimerTask task = queue.peek();if(task.getCurrent() > System.currentTimeMillis()) { //没到执行时间 this.wait(task.getCurrent() - System.currentTimeMillis());} else {task.run();queue.poll();}}}} catch (InterruptedException e) {e.printStackTrace();}});thread.start();}

2.4 总代码

总代码如下:

java">class MyTimerTask implements Comparable<MyTimerTask>{//记录任务private Runnable task = null;//记录执行任务的时刻private long current = 0;public MyTimerTask(Runnable task, long current) {this.task = task;this.current = current;}public Runnable getTask() {return task;}public long getCurrent() {return current;}@Overridepublic int compareTo(MyTimerTask o) {return (int)(this.current - o.current);}public void run() {task.run();}}class MyTimer {private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();public void schedule(Runnable task, long delay) {synchronized (this) {MyTimerTask myTimerTask = new MyTimerTask(task, System.currentTimeMillis() + delay);queue.offer(myTimerTask);this.notify();}}public MyTimer() {Thread thread = new Thread(()-> {try {while(true) {  //循环拿任务,直到任务队列为空synchronized (this) {while (queue.isEmpty()) {  //任务队列为空this.wait();}MyTimerTask task = queue.peek();if(task.getCurrent() > System.currentTimeMillis()) { //没到执行时间this.wait(task.getCurrent() - System.currentTimeMillis());} else {task.run();queue.poll();}}}} catch (InterruptedException e) {e.printStackTrace();}});thread.start();}
}

2.5 测试

如果在main中执行下面这样的代码,也使用schedule方法来打印不同时间执行不同内容,会与上面使用案例的结果一样。

java">public static void main(String[] args) {MyTimer timer = new MyTimer();timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("3000ms后执行");}},3000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("1000ms后执行");}},1000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("2000ms后执行");}},2000);}

结果如下:会按照等待时间由小到大打印内容,并且执行完之后并不会结束,这是因为这些线程是前台线程。


http://www.ppmy.cn/news/1542932.html

相关文章

912.排序数组(桶排序)

目录 题目解法 题目 给你一个整数数组 nums&#xff0c;请你将该数组升序排列。 你必须在 不使用任何内置函数 的情况下解决问题&#xff0c;时间复杂度为 O(nlog(n))&#xff0c;并且空间复杂度尽可能小。 解法 class Solution { public:vector<int> sortArray(vect…

Java应用程序的测试覆盖率之设计与实现(四)-- jacoco-maven-plugin

说在前面的话 加载jacocoagent,开始采集覆盖率数据。 java -javaagent:doc/jacocoagent.jar=includes=com.jacoco.*,output=tcpserver,port=7195,address=172.27.3.242,classdumpdir=classdumpdir/classes/ \ -jar target/jacoco-test-sample.jar. ____ _ …

ELK同时采集Nginx、linux内核日志信息

在logstash服务机器136上安装nginx(配置ELK服务在上一篇文档中&#xff09; 复制之前写的linux内核日志采集配置文档改名字为linux_nginx.conf 编辑linux_nginx.conf 修改完成后启动nginx服务 重新加载linux_nginx.conf配置文件 看到有java进程说明logstash采集成功&#xff0c…

无人机遗传算法详解!

一、遗传算法概述 遗传算法是一种模拟自然选择和遗传机制的优化算法&#xff0c;它仿效生物的进化与遗传&#xff0c;根据生存竞争和优胜劣汰的法则&#xff0c;通过遗传操作&#xff08;选择、交叉、变异&#xff09;&#xff0c;使所求问题的解逐步逼近最优解。 二、无人机…

MemoRAG:重新定义长期记忆的AI问答模型

MemoRAG模型是如何实现长记忆的&#xff1f; ©作者|Blaze 来源|神州问学 引言 随着人工智能的发展&#xff0c;AI问答模型在各种应用场景中表现出色&#xff0c;尤其是在信息检索和知识问答领域。传统的RAG模型通过结合外部知识库的实时检索与生成模型&#xff0c;极大地…

『完整代码』宠物召唤

创建脚本并编写&#xff1a;PetFollowTarget.cs using UnityEngine; public class PetFollowTarget : MonoBehaviour{Transform target;float speed 2f;Animator animator;void Start(){target GameObject.Find("PlayerNormal/PetsSmallPos").gameObject.transform…

高效管理企业客户的SCRM系统方案解析

内容概要 在当今竞争激烈的市场环境中&#xff0c;企业必须找到一种有效的方式来管理与客户的关系&#xff0c;这就是SCRM系统的核心价值所在。SCRM&#xff0c;即社交客户关系管理&#xff0c;不仅仅是传统客户关系管理的延续&#xff0c;它结合了社交媒体和数字化科技&#…

一二三应用开发平台自定义查询设计与实现系列2——查询方案功能实现

查询方案功能实现 上面实现了自定义查询功能框架&#xff0c;从用户角度出发&#xff0c;有些条件组合可以形成特定的查询方案&#xff0c;对应着业务查询场景。诸多查询条件的组合&#xff0c;不能每次都让用户来设置&#xff0c;而是应该保存下来&#xff0c;下次可以直接使…