JavaEE 第11节 定时器

前言

本篇博客重点介绍定时器的简单实现,帮助理解其底层原理。关于JAVA工具类自带的定时器,只会简单介绍,详细使用参阅官方文档(下文中有官方文档的连接)。

一、什么是定时器

定时器的概念非常简单。

它在软件开发中应用非常广泛。它类似于一个提前设定好的闹钟,到达指定时间后,执行设定好的任务程序。比如在网络通信中,如果在一个时间范围内没有得到响应,那么重新尝试连接网络等。

二、Java标准库中的定时器

标准库中提供了Timer类,核心方法是schedule(用于安排任务给计时器),它有两个参数:

  • TimerTask task:描述要执行的任务,实际是对Runnable的封装
  • long delay:延迟多长时间执行 ms

如图(来自官方文档):

代码演示:

一个Timer实例只会创建一个线程(默认创建的前台线程),因此如果有多个schedule的任务,他们是串行执行的 。关于Timer类的详细信息和使用请参阅oracle官方文档:Timer

如果有在多线程环境下使用计时器的需要,需要用到线程池中的ScheduledExecutorService.具体使用请参阅oracle官方文档:ScheduledExecutorService

三、设计实现一个简单的定时器

这里介绍的计时器,只包含一个线程,并且可能会出现任务阻塞(如果一个任务执行时间过长,后执行的任务可能延迟执行)

java 复制代码
/**
 * 我们会使用优先级队列来判断那个任务最紧急需要先完成,所以需要实现Compareble,
 * 然后重写compareTo方法,用时间戳进行大小比较
 * 优先级队列中,存放的是一个一个要执行的任务
 */
class MyTimerTask implements Comparable<MyTimerTask> {

    //用绝对时间------时间戳来表示时间间隔
    long time;

    //要执行的任务
    Runnable runnable;

    //构造方法
    public MyTimerTask(Runnable runnable, long delay) {

        //执行时间等于当前时间+延迟时间大小
        this.time = System.currentTimeMillis() + delay;

        this.runnable = runnable;
    }

    //用于执行任务
    public void run() {
        runnable.run();
    }

    //获取需要执行的任务的执行时间,等一下有用
    public long getTime() {
        return time;
    }

    @Override
    public int compareTo(MyTimerTask o) {
        //创建一个小根堆,时间到的先执行
        return (int) (this.time - o.time);
    }
}


class MyTimer {

    //把schedule的任务放到这里,然后去除优先级大的任务,判断时间,是否需要执行
    PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
    //创建一个锁对象
    private Object locker = new Object();

    public void schedule(Runnable runnable, long delay) {

        //涉及优先级队列的读写操作,需要加锁保证线程安全
        synchronized (locker) {
            //把runnable和delay封装到myTimerTask中
            MyTimerTask myTimerTask = new MyTimerTask(runnable, delay);

            //放入优先级队列
            queue.offer(myTimerTask);

            //通知唤醒myTimer创建的线程
            locker.notify();
        }
    }

    //构造方法,一旦创建这个实例,开始在优先级队列里面循环获取方法执行(如果到达要执行的时机)
    public MyTimer() {
        Thread t = new Thread(() -> {

            while (true) {
                try {
                    //下面涉及对优先级队列的读写操作,需要保证线程安全
                    synchronized (locker) {

                        //目前没有任务,就一直睡,直到schedule了新的任务
                        while (queue.isEmpty()) {
                            locker.wait();
                        }
                        
                        //有了任务,先看看当前需不需要执行
                        MyTimerTask task=queue.peek();
                        
                        //时间已经到了,或者超时了,就执行
                        if(System.currentTimeMillis()>=task.time){
                            task.run();
                            //一处执行完的任务
                            queue.poll();
                        }else{
                            //没到时间,就睡到任务执行的时候
                            locker.wait(task.time-System.currentTimeMillis());
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //启动线程
        t.start();
    }
}

public class Test {
    public static void main(String[] args) throws InterruptedException {
        MyTimer myTimer = new MyTimer();
        myTimer.schedule(() -> {
            System.out.println("hello 1000");
        }, 1000);

        myTimer.schedule(() -> {
            System.out.println("hello 2000");
        }, 2000);

        myTimer.schedule(() -> {
            System.out.println("hello 3000");
        }, 3000);

        System.out.println("程序开始运行");
    }
}

执行结果:

此程序需要注意的几个点:

1)不要把下面的while换成if语句:

2)不能使用Thread.slee()替换wait()

原因有2

1、 Thread.sleep在睡眠的时候是不会释放锁的(抱着锁睡) ,也就是睡眠的时候,schedule操作是无法进行的,会造成死锁

2、notify和wait本来就是搭配起来用,实现线程之间通信的。

如果在myTimer线程睡眠的时候,有新的任务进来,notify会重新唤醒线程,而Thread.sleep是没有这个功能的。

相关推荐
kunkun101几秒前
Mybatis的XML实现方法
xml·java·mybatis
libai7 分钟前
STM32 USB HOST CDC 驱动CH340
java·前端·stm32
IT学长编程8 分钟前
计算机毕业设计 数字化农家乐管理平台的设计与实现 Java实战项目 附源码+文档+视频讲解
java·spring boot·毕业设计·毕业论文·计算机毕业设计选题·计算机毕业设计开题报告·农家乐管理平台
Q186000000008 分钟前
用java语言写一个表的查询操作
java·开发语言·oracle
2401_857600959 分钟前
心理教育辅导系统的设计与Spring Boot实现
java·spring boot·后端
GGBondlctrl10 分钟前
【后端开发】JavaEE初阶—线程的理解和编程实现
java·java-ee·线程的创建方式·线程是什么·线程的内部原理
没什么技术21 分钟前
java实现LRU 缓存
java·算法·lru
我爱豆子26 分钟前
Leetcode Hot 100刷题记录 -Day19(回文链表)
java·算法·leetcode·链表
南斯拉夫的铁托36 分钟前
(PySpark)RDD实验实战——取最大数出现的次数
java·javascript·spark
☼YJLH☾38 分钟前
第十章,XML
xml·java·后端·intellij-idea