@TOC


什么是定时器

  • 定时器是多线程编程中的一个重要的组件
  • 好比一个闹钟 定好一个时间让一个线程去执行
  • 定时器在网络编程中特别常见

比如:

在我们打开浏览器,随便打开一个网页
如果顺利,马上就能进入网站

但是!有时候由于网络不稳定,或者网站的服务器崩了
那么,此时这里就会一直转圈圈加载。
但是浏览器不会一直都在这里等待访问的网站反馈信息。
它里面其实有一个等待 的“超时时间”,过了这个时间,它就不会再等待,直接跟你说“访问失败 / 网站不见了”


此时我们很快就能想到 join,有一个用法,在括号里添加指定的 “超时时间”。
sleep也可以达到这个效果,sleep(指定休眠时间) 。
join 和 sleep 都是基于系统内部的定时器,来实现的

那么,我们就可以使用 代码中的定时器,来实现类似的功能。

先介绍标准库的定时器用法,然后再看看如何自己实现一个定时器


标准库的定时器用法

Timer <-----> java.util.Timer

核心方法schedule

schedule,它的功能就跟 它 中文意思一样。
每调用一次 schedule‘,就会给定时器 安排一个任务。
通过这个方法,就可以把 任务 注册到 定时器内部。
而计数器内部是支持 注册 多个任务的。

schedule方法,有两个参数

  1. 任务
  2. 多长时间之后执行

image-20220509170539546


如何自己实现一个定时器

一个问题:Timer 类 的内部需要什么东西?

Timer 的 工作内容入手

  1. 管理很多的任务
  2. 执行时间到了的任务

管理任务又可以细分为 2个:

1、描述任务(创建一个专门的类来表示一个定时器中的任务【Timer Task】)
2、组织任务(使用一定的数据及结构进行组织数据,把一些任务放到一起。)

具体任务顺序为
1、描述任务(创建一个专门的类来表示一个定时器中的任务【Timer Task】)
2、组织任务(使用一定的数据及结构进行组织数据,把一些任务放到一起。)
3、执行时间到了的任务


1. 描述任务

// 创建一个类,来描述一个具体的任务
class MyTask{
    // 任务具体要做什么
    private Runnable runnable;
    // 任务具体的执行时间:保存任务要执行的毫秒级时间戳
    private long time;
    // after 是一个时间间隙,不是绝对的时间戳的值
    public MyTask(Runnable runnable,long after){
        this.runnable = runnable;
        // 很简单,意思就是从当前时间开始, after 秒之后,这个任务被执行。
        this.time = System.currentTimeMillis()+after;
    }
    // 通过调用这里 run方法,来执行我们任务具体要做什么
    public void run(){
        runnable.run();
    }
}

2. 组织任务

image-20220509170750080


3. 执行时间到了的任务

需要先执行时间最靠前的任务

比如:十分钟后,大家去休息一下。
但是,当前的我们无法判断这个时间,所以我们需要用一个线程去不断的去检查当前优先队列的首元素,看看当前最靠前的这个任务是不是时间到了

通过 自己构造的 Mytimer 计时器类 的 构造方法。
创建一个线程,帮助我们来进行一个检查

image-20220509170905709


上述代码中存在两个非常严重的问题!!!

image-20220509171011331


代码

import java.util.concurrent.PriorityBlockingQueue;

// 创建一个类,来描述一个具体的任务
class MyTask implements Comparable<MyTask>{
    // 任务具体要做什么
    private Runnable runnable;
    // 任务具体的执行时间:保存任务要执行的毫秒级时间戳
    private long time;
    // after 是一个时间间隙,不是绝对的时间戳的值
    public MyTask(Runnable runnable,long after){
        this.runnable = runnable;
        // 很简单,意思就是从当前时间开始, after 秒之后,这个任务被执行。
        this.time = System.currentTimeMillis()+after;
    }
    // 通过调用这里 run方法,来执行我们任务具体要做什么
    public void run(){
        runnable.run();
    }

    public long getTime() {
        return time;
    }

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

class MyTimer{
    // 定时器内部要能够存放多个任务
    private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
    public void schedule(Runnable runnable,long after){
        MyTask task = new MyTask(runnable,after);
        queue.put(task);
        synchronized(locker){
            locker.notify();
        }
    }
    private Object locker = new Object();

    public MyTimer(){
        Thread t = new Thread(()->{
           while(true){
               try {
                   // 取出队首元素
                   MyTask task =queue.take();
                   //再比较一下看看当前这个任务时间到了没
                   long curTime = System.currentTimeMillis();
                   // 拿当前时间 和 任务执行时间进行比较
                   if(curTime < task.getTime()){
                       //时间还没到,把任务再塞回到队列中
                       queue.put(task);
                       // 指定一个等待时间
                       synchronized(locker){
                          locker.wait(task.getTime() - curTime);
                       }
                   }else{
                       // 时间到了,执行这个任务
                       task.run();
                   }
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        });
        t.start();
    }
}

public class Test25 {
    public static void main(String[] args) {
        MyTimer myTimer = new MyTimer();
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello timer");
            }
        },3000);
        System.out.println("main");
    }
}