简单实现Java定时器

article/2025/11/7 2:25:33

✨✨hello,愿意点进来的小伙伴们,你们好呐!
🐻🐻系列专栏:【JavaEE】
🐲🐲本篇内容:自己实现Java定时器
🐯🐯作者简介:一名现大二的三非编程小白,日复一日,仍需努力。

  • 1. 什么是定时器
  • 2. Java内置定时器的常用功能
  • 3.自定义定时器
    • 3.1 实现定时器思路
    • 3.2 MyTimerTask 类:
    • 3.3 MyTimer 类:
    • 执行 :
    • 3.4 定时器的问题 :
      • 3.4.1 忙等 :
      • 3.4.2 CPU随机调度导致的bug

1. 什么是定时器

在日常生活中,如果我们想要在 t1 后去做一件重要的事情,那么为了防止忘记,我们就可以使用闹钟的计时器功能,规定了 t1 时间后提醒我们去执行这件事情. — 这就是Java定时器的简单功能


2. Java内置定时器的常用功能

Java中的定时器的类是 : Timer ,为util包中的一个无继承关系的类, 从该类的构造方法中,我们可以使用无参构造器创建该类的对象,也可以在创建类对象的时候指定定时器中所需要的线程的名字,与是否为守护进程
在这里插入图片描述


在定时器中最常用的方法就是 schedule(TimerTask task, long delay)

该方法传参的是一个 TimerTask 对象,与定时器约定的执行时间间隔 delay


而 TimerTask 类则是一个来描述计时器任务的类,该类中有 抽象方法 run(),所以我们给 schedule 传参中的 TimerTask 对象都会重写 run() 方法,然而 重写的run() 方法中的语句,则是定时器需要执行的语句.

在这里插入图片描述


而 delay 则是我们约定从当前时间后的 delay 内执行传入的任务.时间单位为 毫秒


接下来我们来看一个简单的定时器的使用 :

我们在创建定时器的时候指定了定时器中的扫描线程的线程名,然后使用 schedule 方法传入任务与任务执行的间隔时间 2000 毫秒
这个时候在执行该代码的2000毫秒后,定时器就会将该任务执行

public class TimerTest {public static void main(String[] args) {//创建定时器的时候指定定时器中内置线程的名字Timer timer = new Timer("内置线程");timer.schedule(new TimerTask() {@Overridepublic void run() {//Thread.currentThread().getName() 执行到该任务的线程名System.out.println(Thread.currentThread().getName() + " hello");}},2000);}
}

在上述说到有一个定时器内置的扫描线程

这个扫描线程是怎么回事呢?又在定时器中起到什么作用呢?在接下来的自己实现定时器中就会介绍到.

3.自定义定时器

3.1 实现定时器思路

1.实现定时器,我们首先需要有一个可以来描述定时器中的任务的类 MyTimerTask
2.需要使用一个数据结构将定时器中的任务按照执行时间的顺序给组织起来
3.在 MyTimer 定时器类中会有一个线程不断地去访问定时器的任务,查看是否到了指定执行时间.


3.2 MyTimerTask 类:

在MyTimerTask类中,我是这样设计的:
1.实现了Runnable接口与Comparable接口,Comparable是来重新指定比较规则.
2.在类中编写了抽象方法 run(),让创建该类对象的时候可以重新run()方法,用来规划任务内容.
3.nextExecutionTime是来记录当前任务执行的时间是什么时候,并附有nextExecutionTime的set与get方法

import java.util.Comparator;/*** @author 罗鸿基* @version 1.0* 这个是一个描述线程任务的类* 该类中需要有*/
public abstract class MyTimerTask implements Runnable, Comparable<MyTimerTask> {//使用抽象方法 run 方法来让创建该线程的重写 run 方法public abstract void run();//该任务执行的时间protected long nextExecutionTime;public MyTimerTask() { }public long getNextExecutionTime() {return nextExecutionTime;}public void setNextExecutionTime(long nextExecutionTime) {this.nextExecutionTime = nextExecutionTime;}//因为是使用优先级队列,所以就要重写compareTo方法,制定比较规则@Overridepublic int compareTo(MyTimerTask o) {return (int) (this.getNextExecutionTime() - o.getNextExecutionTime());}
}

3.3 MyTimer 类:

MyTimer 类设计如下 :
1.该类中需要有一个用来组织任务的数据结构,我采用优先级阻塞队列来实现组织任,因为PriorityBlockingQueue 实现了BlockingQueue接口,可以当为线程安全的队列,而我们需要对每一个任务按照执行时间的顺序进行排序.所以我选择PriorityBlockingQueue来实现该功能
2.在该类中,我们需要有一个线程,不断地对任务队列中优先级最高(最快执行)的任务进行查看, 看是否到达执行时间.
3.在schedule方法中,我们需要修改任务中的执行时间,并将任务插入任务队列

package csdn;import java.util.concurrent.PriorityBlockingQueue;/*** @author 罗鸿基* @version 1.0* 这个是定时器类* 需要有一个用来组织任务的数据结构 -- PriorityBlockingQueue*/
public class MyTimer {//该队列用来组织任务//因为有可能有多个线程去访问任务队列,使用就使用PriorityBlockingQueue (阻塞队列)//因为也需要判断任务的优先级(执行的时间间隔差异),所以我们所以优先级队列private PriorityBlockingQueue<MyTimerTask> queue = new PriorityBlockingQueue<>();//扫描线程private Thread thread;public MyTimer() {//该线程用来不断扫描任务队列中是否有可执行的任务thread = new Thread(() -> {while (true) {try {//取出当前队列中执行时间间隔最短的任务//如果队列中没有任务,那么就一直等待MyTimerTask take = queue.take();//取出任务执行的时间long nextExecutionTime = take.getNextExecutionTime();//取出系统当前的时间long newTime = System.currentTimeMillis();//两个时间对比,看是否到达执行时间if (nextExecutionTime > newTime) {//未到执行时间//将任务重新放入队列queue.put(take);}else {//到达执行时间,就执行任务take.run();}} catch (InterruptedException e) {e.printStackTrace();}}});//启动扫描线程thread.start();}public void schedule(MyTimerTask task,long delay) {//以防别的线程同时往定时器中插入任务synchronized (queue) {//修改任务中的时间task.setNextExecutionTime(System.currentTimeMillis() + delay);//将任务插入队列queue.put(task);}}
}

执行 :

public class TimerTest {public static void main(String[] args) {MyTimer myTimer = new MyTimer();myTimer.schedule(new MyTimerTask() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " 延迟1000毫秒的任务 hello");}},1000);myTimer.schedule(new MyTimerTask() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " 延迟2000毫秒的任务 hello");}},2000);myTimer.schedule(new MyTimerTask() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " 延迟3000毫秒的任务 hello");}},3000);}
}

执行结果符合我们的预期
在这里插入图片描述


我们可以看到当前Java进程中的确有一个线程一直在工作,该线程就是扫描线程

在这里插入图片描述


且当前线程是属于等待状态

在这里插入图片描述


但是的确的定时器其实是有不小的问题的,我们下面来分析问题并解决

3.4 定时器的问题 :

3.4.1 忙等 :

如果队列中没有任务,那么取出任务会就一直等待,那么就还不会造成忙等,但是如果是队列中有任务的话,当前线程就会不断循环,取出任务来进行查看.这种行为是很浪费CPU资源的,下面我们来进行优化

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


3.4.2 CPU随机调度导致的bug

对于多线程代码中,CPU的随机调度是万恶之源,会导致很多意想不到的bug出现.


下面我们来举个例子来解释一下CPU随机调度带来的bug :

例如 : 在某一时刻线程线程从队列中取出任务的执行时间为14:00(下图中红色的的线所指向),然后这个时候扫描线程被操作系统调度后不再继续执行
而此时又有一个线程去调用了schedule() 方法,加入一个任务,这个任务的执行时间为13:30.然后加入任务队列,执行notify()
那么这个时候操作系统再继续调度到取出任务后,发现距离任务执行的时间还有一段距离,这个时候就调用wait(time)方法
然后接下来没有任务加入了,就不会调用notify()方法,那么线程就加入等待状态,等待到14.00再唤醒,那么这个时候就会完美地错过了新加入线程的执行时间

在这里插入图片描述


图解:
在这里插入图片描述


代码实现:

public MyTimer() {//该线程用来不断扫描任务队列中是否有可执行的任务thread = new Thread(() -> {while (true) {synchronized (lock) {try {//取出当前队列中执行时间间隔最短的任务MyTimerTask take = queue.take();//取出任务执行的时间long nextExecutionTime = take.getNextExecutionTime();//取出系统当前的时间long newTime = System.currentTimeMillis();//两个时间对比,看是否到达执行时间if (nextExecutionTime > newTime) {//未到执行时间//将任务重新放入队列queue.put(take);lock.wait(nextExecutionTime - newTime);} else {//到达执行时间,就执行任务take.run();}} catch (InterruptedException e) {e.printStackTrace();}}}});

http://chatgpt.dhexx.cn/article/beIIGoBe.shtml

相关文章

java 定时器框架_java定时器

java定时器 什么是Java定时器&#xff1f; Java 定时器就是在给定的间隔时间执行自己的任务; Java实现定时器有以下几种: 通过Timer来实现定时任务 Timer 是来自 java.util.Timer 指定时间执行任务 /** * author spp * date 2020-10-14 09:04 **/ public class TimerTest { pub…

Java定时器Timer的使用

目录 一、Timer常用方法 1、在某个时间点执行一次任务 2、在某个时间点执行一次任务&#xff0c;接着每隔X秒执行一次任务 3、在N秒后执行一次任务 4、在N秒后执行一次任务&#xff0c;接着每隔X秒执行一次任务 二、Timer的多任务模式 一、Timer常用方法 Timer应用场景&a…

Java定时器选择

java计时器和死循环哪个好&#xff1f;哪个建议使用&#xff1f; 计时器性能更好&#xff0c;但是写起来稍微复杂一点。如果是非常短暂的延迟&#xff0c;用死循环也未尝不可。一般来说能不用死循环的尽量不用死循环&#xff01;如果你使用的是JDK1.5以上的&#xff0c;可以使…

9.java定时器

java定时器 java的定时器有四种实现方式 使用java的timer类&#xff0c;这种方式可以灵活的控制定时器的开启关闭使用线程和线程池的方法&#xff0c;这种方式对于关闭定时器是不优雅的&#xff0c;甚至可能出错使用spring注解来启动定时任务&#xff0c;使用起来简单&#xff…

Java当中的定时器

目录 一、什么是定时器 二、Java当中的定时器 ①schedule()方法&#xff1a; ②TimerTask ​编辑 ③delay 三、实现一个定时器 前提条件: 代码实现: ①确定一个“任务”&#xff08;MyTask)的描述&#xff1a; ②schedule方法&#xff1a; ③需要一个计时器 属性…

【Java定时器】: Java创建定时器的三种方式(详细讲解)

Java创建定时器的三种方式 第一种&#xff0c;常见的thread&#xff0c;创建一个Thread让他让循环里一直执行&#xff0c;通过 Thread.sleep 来达到 定时任务的效果。 栗子如下&#xff1a; public static void main(String[] args) {final long timeTnterval 1000;Runnable …

Java定时器

目录 一、认识定时器 1、什么是定时器 2、标准库的定时器 二、模拟实现定时器 1、描述定时器中的任务 2、管理多个任务 3、扫描线程 4、优化 5、最终代码 一、认识定时器 1、什么是定时器 定时器是实际开发中常用的一个重要组件&#xff0c;类似于我们生活中的“闹钟…

matlab画图函数之plot【matlab图行绘制一】

plot函数 plot(x,y,’–gs’,‘LineWidth’,2,‘MarkerSize’,10,‘MarkerEdgeColor’,‘b’,‘MarkerFaceColor’,[0.5,0.5,0.5]) plot函数是最基本、最常用的绘图函数&#xff0c;用于绘制线性二维图。有多条曲线时&#xff0c;循环使用由坐标轴颜色顺序属性定义的颜色&…

Matlab画图线型、符号及颜色设置

1. matlab 中线条的主要属性 Color: 颜色LineStyle: 线型LineWidth: 线宽Marker: 标记点的形状MarkerFaceColor: 标记点填充颜色MarkerEdgeColor: 标记点边缘颜色MarkerSize: 标记点大小 2. 各种属性的名称 2.1 线型 -Solid line (default) – Dashed line : Dotted line …

matlab 画图基本

内容安排如下&#xff1a; 1、基本绘制&#xff08;图画大小、图形名称、图画背景、坐标轴名称、刻度范围、曲线颜色、坐标轴字体颜色等&#xff09;2、多条曲线&#xff08;plot hold on&#xff1b;plotyy&#xff1b;subplot&#xff1b;&#xff09;3、日期及时间轴绘图4、…

MATLAB画图详细教程

本文将详细介绍如何用matlab绘图并美化。 关于figure() 创建图窗窗口:figure() figure()的属性: Name:在标题栏显示的名称,接字符串,如Test Position:在电脑屏幕上的位置和大小,后接向量[left,bottom,width,height]也就是说指定了图窗的左下角位置,再向右+width、…

MATLAB画图总结

前言 在进行数据处理展示的时候&#xff0c;为了能直观体现实验的结果&#xff0c;需要进行绘图&#xff0c;让人们能直观的记住数据的走向特征&#xff0c;图像是结果的一种可视化展现&#xff0c;因此掌握一些绘图方法非常重要&#xff0c;使用MATLAB可以很简单的进行画图。下…

matlab 画图基本介绍

1.在命令窗口输入命令时&#xff0c;可以不必每输入一条命令就按enter键执行&#xff0c;可以在输入几行后一同运行。方法是&#xff1a;换行时&#xff0c;只要在按住<shift>键的同时按<enter>键即可&#xff0c;否则matlab就会执行上面输入的所有语句。 2.如何将…

matlab画图操作(修改坐标轴及字体,加粗,颜色修改,适合论文画图)

matlab常用画图操作 1.设置坐标轴2.设置figure大小3.matlab线条设置4.子图设置5.颜色查询6.colorbar设置7.线条透明度设置8.设置坐标轴刻度形式&#xff08;对数刻度&#xff09;9.图例设置10 文件保存11 消除白色边框12 添加子标题13 调换y轴递增顺序 1.设置坐标轴 %设置坐标…

使用matlab画图中图

又到一年论文季&#xff0c;没想到临近投稿的我居然会被图片的清晰度打败&#xff0c;需要子母图的时候&#xff0c;我直接使用powerpoint进行拼接&#xff0c;多次png另存为&#xff0c;图已经糊出了新高度&#xff0c;那种超级糊图在论文里应该是投不出去的吧。。。但是&…

matlab-画图对坐标的显示

前言 许多小朋友对于matlab画图函数再熟悉不过了&#xff0c;但是画图里面还有更细小的地方我们还得注意&#xff0c;对于坐标的显示也是我们在日常生活中常需要我们做的&#xff0c;下面我就将以一个例子1来说明在画图中显示坐标的两种形式。 下面的数据取样格式为 提示&a…

Matlab画图相关知识

目录 一、绘制不同种类的图像 1.1.画一般曲线图 1.2.绘制柱状图 二、matlab的图像处理 2.1将多张图同时绘制在一个Figure上面&#xff0c;采用subplot函数 2.2改变matlab图像坐标刻度增长幅度 2.3设置坐标轴刻度为任意值 2.4在一张Figure中用同一个x对应两个y作图 2.5关于…

MATLAB画图使用不同的线型、点及标记

转载自&#xff1a;MATLAB画图使用不同的线型、点及标记 (baidu.com) 一、 线型、连续标记 先从最普通的说起。在plot函数中指定线型。 tlinspace(0,5,20); x1 t; x2 2*t; x3 3*t; x4 4*t; plot(t,x1,b,t,x2,g-o,t,x3,r*,t,x4,c:d); 这是基础的比较简单的情况。不做…

MATLAB绘图

在MATLAB中绘制函数图形的步骤如下&#xff1a; 先定义变量 x&#xff0c;通过指定的变量 x 值的范围&#xff0c;该函数被绘制&#xff1b; 然后定义函数&#xff0c; y f(x)&#xff1b; 最后调用 plot 命令&#xff0c;如 plot(x, y)。 接下来我们通过例子绘制简单的函…

matlab画图入门教程

**matlab画图&#xff1a;**图像是数据结果的一种可视化表现&#xff0c;它能直观的体现你的数据结果&#xff0c;并且能体现你获得结果的准确性&#xff0c;在当前的大数据时代&#xff0c;在做数据分析的时候&#xff0c;将其可视化可以直观多维的展示数据&#xff0c;可以让…