【JavaEE】定时器

【JavaEE】定时器

  • 一、什么是定时器
  • 二、标准库中的定时器
      • [2.1 Timer类的核心方法schedule](#2.1 Timer类的核心方法schedule)
      • [2.2 Timer类的构造方法](#2.2 Timer类的构造方法)
  • 三、模拟实现定时器
      • [3.1 创建一个类,表示一个任务](#3.1 创建一个类,表示一个任务)
      • [3.2 创建一个集合类,用来管理任务](#3.2 创建一个集合类,用来管理任务)
      • [3.3 创建schedule方法,将任务方到队列当中](#3.3 创建schedule方法,将任务方到队列当中)
      • [3.4 创建线程,执行对列中的任务](#3.4 创建线程,执行对列中的任务)
      • [3.5 代码优化](#3.5 代码优化)
  • 四、扩展

博客结尾有此篇博客的全部代码!!!

一、什么是定时器

定时器(Timer)是一种用于在特定时间或按照一定时间间隔执行任务的工具。它的核心功能是允许程序在指定的时间点或周期性地执行代码,而不需要程序一直轮询等待(是一种通过周期性检查某个条件是否满足来等待事件发生的技术)。

通俗来讲:即在设定的时间时刻执行某事的设备(例如闹钟,在指定的时间响铃),Java中的定时器会在到达设定的时间后,执行指定的代码。

二、标准库中的定时器

2.1 Timer类的核心方法schedule

标准库中提供定时器的类是Timer类,Timer类的核心方法是schedule;

timer.schedule(TimerTask task,Date time);

timer.schedule(TimerTask task,long delay,long period);

timer.schedule(TimerTask task,Date firstTime,long period);

2.2 Timer类的构造方法

java 复制代码
public Timer() {
    this("Timer-" + serialNumber());
}

以 Timer- +序列号作为定时器的名字。

java 复制代码
 public Timer(boolean isDaemon) {
        this("Timer-" + serialNumber(), isDaemon);
    }

是否将该定时器作为守护线程执行。

java 复制代码
 public Timer(String name) {
        this(name, false);
    }

以name作为定时器的名字。

java 复制代码
public Timer(String name, boolean isDaemon) {
        var threadReaper = new ThreadReaper(queue, thread);//可能是用于管理线程的生命周期
        this.cleanup = CleanerFactory.cleaner().register(this, threadReaper);//可能是用于创建和管理资源的清除器
        thread.setName(name);
        thread.setDaemon(isDaemon);
        thread.start();
    }

设置线程的名称和守护状态,并启动线程以执行定时任务。

三、模拟实现定时器

思路:

  1. 创建一个类,表示一个任务
  2. 创建一个集合类,用来管理任务
  3. 创建schedule方法,将任务方到队列当中
  4. 创建线程,执行对列中的任务

3.1 创建一个类,表示一个任务

java 复制代码
class MyTimerTask implements Comparable<MyTimerTask> {

    private Runnable runnable;
    private long time;

    //初始化TimerTask任务
    public MyTimerTask(Runnable runnable, long time) {
        this.runnable = runnable;
        this.time = time;
    }

    public void run(){
        runnable.run();
    }
    public long getTime() {
        return time;
    }

    @Override
    public int compareTo(MyTimerTask o) {
        return (int) (this.time-o.time);
    }
}

getTime()方法:获取任务执行的时间戳(和当前时间的时间戳进行比较)

compareTo()方法:创建优先级队列,需要将距离当前时间近的任务排在靠前位置。

3.2 创建一个集合类,用来管理任务

java 复制代码
private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();

3.3 创建schedule方法,将任务方到队列当中

java 复制代码
    //实现schedule方法,将任务放入队列当中
    public void schedule(Runnable runnable, long time){
        synchronized (lock){
            queue.offer(new MyTimerTask(runnable, time));
        }
    }

3.4 创建线程,执行对列中的任务

java 复制代码
    //创建线程,执行对队列中的任务
    public MyTimer(){
        Thread t = new Thread(()->{
            while(true){
                synchronized (lock){
                    while(queue.isEmpty()){
                        continue;
                    }
                    MyTimerTask task=queue.peek();
                    //获取当前时间的时间戳
                    long curTime = System.currentTimeMillis();
                    if(task.getTime()>curTime){
                        continue;
                    }else{
                        task.run();
                        queue.poll();
                    }
                }
            }
        });
        t.start();
    }

3.5 代码优化

如果当前任务未到执行时间时,代码会不断重复执行队列的取出和塞回操作,这种现象被称为"忙等"。为了更有效地利用CPU资源,我们需要使用阻塞式等待而不是忙等。

第一时间我们会想到用sleep() 来阻塞等待,但是这里由于任务执行的不确定性,我们不知道设置多长时间,设置短了没效果,设置长了,当唤醒的时候,第一个代码已经过了执行时间该怎么办?

这里就要用到刚学不就得wait()和notify()!!!

java 复制代码
import java.util.PriorityQueue;

class MyTimerTask implements Comparable<MyTimerTask> {

    private Runnable runnable;
    private long time;

    //初始化TimerTask任务
    public MyTimerTask(Runnable runnable, long time) {
        this.runnable = runnable;
        this.time = time;
    }

    public void run() {
        runnable.run();
    }

    public long getTime() {
        return time;
    }

    @Override
    public int compareTo(MyTimerTask o) {
        return (int) (this.time - o.time);
    }
}

class MyTimer {
    Object lock = new Object();
    //创建优先级队列,按时间顺序顺序执行
    private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();

    //创建线程,执行对队列中的任务
    public MyTimer() {
        Thread t = new Thread(() -> {
            while (true) {
                try {
                    synchronized (lock) {
                        while (queue.isEmpty()) {
                            lock.wait();
                        }
                            MyTimerTask task = queue.peek();
                            //获取当前时间的时间戳
                            long curTime = System.currentTimeMillis();
                            if (task.getTime() > curTime) {
                                lock.wait(task.getTime() - curTime);
                            } else {
                                task.run();
                                queue.poll();
                            }
                        }
                    } catch(InterruptedException e){
                        throw new RuntimeException(e);
                    }
                }
            });
            t.start();
        }

                //实现schedule方法,将任务放入队列当中
        public void schedule (Runnable runnable,long time){
            synchronized (lock) {
                queue.offer(new MyTimerTask(runnable, time));
                lock.notify();
            }
        }
    }

主要优化了两段代码:

  • schedule中的加入notify()来唤醒wait()方法
  • 将"忙等"优化掉,更有效的利用资源

四、扩展

上述模拟实现定时器适合执行时间相差较大任务,如果在这个时间段有大量集中的任务需要上述代码实现的定时器处理,此时就适合用基于循环数组实现的定时器处理!(用二维链表实现的,将时间段划分为一小块一小块,分别将各个时间段的任务放入链表中)

定时器默认是单线程执行任务,如果一个任务执行时间过长,可能一起阻塞等待,或者所有任务共享同一个线程,可能会导致资源竞争问题。

那么这里我们就可以用我们刚学的线程池来帮助我们解决这个问题!

java 复制代码
 ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);

此篇博客的全部代码!!!

相关推荐
无敌最俊朗@19 分钟前
MQTT 关键特性详解
java·前端·物联网
JAVA学习通19 分钟前
微服务项目->在线oj系统(Java-Spring)----[前端]
java·开发语言·前端
拾贰_C26 分钟前
【SpringBoot】前后端联动实现条件查询操作
java·spring boot·后端
GUIQU.2 小时前
【QT】嵌入式开发:从零开始,让硬件“活”起来的魔法之旅
java·数据库·c++·qt
callJJ6 小时前
从 0 开始理解 Spring 的核心思想 —— IoC 和 DI(2)
java·开发语言·后端·spring·ioc·di
wangjialelele6 小时前
Linux中的线程
java·linux·jvm·c++
谷咕咕6 小时前
windows下python3,LLaMA-Factory部署以及微调大模型,ollama运行对话,开放api,java,springboot项目调用
java·windows·语言模型·llama
没有bug.的程序员7 小时前
MVCC(多版本并发控制):InnoDB 高并发的核心技术
java·大数据·数据库·mysql·mvcc
在下村刘湘7 小时前
maven pom文件中<dependencyManagement><dependencies><dependency> 三者的区别
java·maven
不务专业的程序员--阿飞8 小时前
JVM无法分配内存
java·jvm·spring boot