定时器

定时器

定时器是什么

定时器也是软件开发中的一个重要组件. 类似于一个 “闹钟”. 达到一个设定的时间之后, 就执行某个指定好的代码.
定时器是一种实际开发中非常常用的组件.
比如网络通信中, 如果对方 500ms 内没有返回数据, 则断开连接尝试重连.
比如一个 Map, 希望里面的某个 key 在 3s 之后过期(自动删除).
类似于这样的场景就需要用到定时器.

标准库中的定时器

标准库中提供了一个 Timer 类. Timer 类的核心方法为 schedule .
schedule 包含两个参数. 第一个参数指定即将要执行的任务代码, 第二个参数指定多长时间之后执行 (单位为毫秒).

 public static void main(String[] args)
    {
        Timer timer=new Timer();
        //给timer中注册的这个任务,不是在调用schedule的线程中执行的,而是通过Timer内部的线程来负责执行的
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定时器开始执行");
            }
        },3000);//这段代码代表程序开始三秒之后开始打印出  定时器开始执行  这句话
        System.out.println("程序开始执行");
    }

结果
定时器_第1张图片
我们发现程序并没有结束,为什么呢?
是因为Timer内部有自己的线程,为了保证随时可以处理新安排的任务,这个线程会持续执行,并且这个线程还是一个前台线程

一个定时器中可以有多个任务的,定时器会根据这些任务设置的时间来合理的分配执行顺序

    public static void main(String[] args)
    {
        Timer timer=new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("3");
            }
        },3000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("2");
            }
        },2000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("1");
            }
        },1);
        System.out.println("程序开始执行");
    }

我们可以看到,虽然先给 3 分配了任务,但是任务 1 先执行了,是因为任务3的时间比任务1长

实现定时器

思路
定时器_第2张图片
优化
定时器_第3张图片

相关问题
定时器_第4张图片
定时器_第5张图片
定时器_第6张图片

class MyTimerTask implements Comparable<MyTimerTask>
{
    //表示任务啥时候执行,这是一个毫秒级时间戳
    private long time;
    //表示任务具体是啥
    private Runnable runnable;
    public MyTimerTask(Runnable runnable,long delay)
    {
        //delay是一个相对的时间差,例如3000这样的数值
        //构造time是要根据当前系统时间和delay进行构造
        time=System.currentTimeMillis()+delay;
        this.runnable=runnable;
    }

    public long getTime()
    {
        return time;
    }

    public Runnable getRunnable()
    {
        return runnable;
    }

    @Override
    public int compareTo(MyTimerTask o)
    {
        //认为时间小的优先级高,最终时间最小的元素就会放到队首
        return (int)(this.time-o.time);
    }
}
//定时器类的本体
class MyTimer
{
    //用来加锁的对象
    private Object locker=new Object();
    //使用优先级队列来保证上述的N个任务
    private PriorityQueue<MyTimerTask> queue=new PriorityQueue<>();
    //定时器的核心方法,就算把要执行的任务添加到队列中
    public void schedule(Runnable runnable,long delay)
    {
        synchronized (locker)
        {
            MyTimerTask task=new MyTimerTask(runnable,delay);
            //这个集合类不是线程安全的,即会在主线程中使用,又会在扫描线程中使用,这就触发了线程安全问题,两个线程针对同一个集合作用,因此要加锁
            queue.offer(task);
            //每次来新的任务,都唤醒一下之前的扫描线程,重新规划等待时间
            locker.notify();
        }
    }
    //MyTimer中还需要构造一个”扫描线程“,一方面去负责监控队首元素是否到点了,是否应该执行
    //另一方面当任务到点之后,就要调用这里的Runnable的run方法来完成任务
    public MyTimer()
    {

        Thread t=new Thread(()->{
            while(true)
            {
                try
                {
                    synchronized (locker)
                    {
                        //扫描线程中几乎都会涉及到queue,因此全加锁
                        while(queue.isEmpty())
                        {
                            //如果当前队列为空,此时就不应该去取这里的元素
                            //continue;
                            //此处使用wait等待更合适,如果使用continue,就会使这个线程while循环运行的飞快
                            //也会陷入一个高频占用cpu的状态(忙等)
                            locker.wait();
                        }
                        MyTimerTask task=queue.peek();
                        long curTime=System.currentTimeMillis();
                        if(curTime>=task.getTime())
                        {
                            //假设当前时间是12:01,任务时间是12:00,此时就意味着要执行这个任务了
                            //执行任务之后把这个任务从队列中去除掉
                            queue.poll();
                            //执行当前这里的任务的内容
                            task.getRunnable().run();
                        }
                        else
                        {
                            //如果任务时间没到
                            //让当前扫描线程休眠一下,按照时间差来休眠
                            //Thread.sleep(task.getTime()-curTime);
                            locker.wait(task.getTime()-curTime);
                        }
                    }

                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }
}
//写一个定时器
public class Demo22
{

    public static void main(String[] args)
    {
        MyTimer timer=new MyTimer();
        timer.schedule(new Runnable() {
            @Override
            public void run()
            {
                System.out.println("任务3");
            }
        },3000);
        timer.schedule(new Runnable() {
            @Override
            public void run()
            {
                System.out.println("任务2");
            }
        },2000);
        timer.schedule(new Runnable() {
            @Override
            public void run()
            {
                System.out.println("任务1");
            }
        },1000);
        System.out.println("任务开始执行:");//
    }
}

你可能感兴趣的:(java,数据库,开发语言)