定时器(一个小例子)

定时器

类似于前面讲过的Runnable,schedule这个方法会记录时间,当前任务什么时候去执行.

现在虽然代码执行完了,但是线程并没有结束

Timer内部,有自己的线程.为了保证随时可以处理新安排的任务,这个线程会继续执行.并且这个线程是前台线程.这样就可以更好的说明timer中的任务是在timer内部线程执行的.

定时器的自行实现

分析:

首先一个定时器中是有很多个任务的,先要能够把一个任务给描述出来,在使用数据结构把多个任务组织起来

1.创建一个TimerTask这样的类,表示一个任务,这个任务就需要包含两方面

(1)任务的内容

(2)任务的实际执行时间

这个可以用时间戳出来表示,在schedule的时候,先获取到当前的系统的时间,在这个基础上,加上delay时间间隔.得到了真实要执行这个任务的时间

2.使用一定的数据结构,把多个TimerTask给组织起来

由于我们并不知道将来要创建多少个任务,所以这里使用List来进行组织,但是如果任务非常多,如何确定哪个任务何时能够执行呢?这样就需要搞一个线程,不停的对上述的List进行遍历,看这里的每个元素,是否到了时间,时间到就执行,时间不到,就跳过下一个.

但是注意,这个思路并不科学!!如果这些任务的时间都还为时尚早,在时间到达之前,此处的这个扫描线程就需要一刻不停的反复扫描.

优化如下

1.我们并不需要扫描所有的任务

只需要盯住,时间最靠前的任务即可,最早的任务时间还没到的话,其它的任务时间更不会到

遍历所有任务=>只关注一个

那么我们如何知道哪个任务是最靠前的?如何知道哪个任务是最小的?

堆/优先级队列

2.针对这一个任务的扫描,也不必一直反复执行

而是在获取到队首元素的时间之后,和当前的系统时间做一个差值,根据这个差值,来决定休眠/等待(不会消耗CPU资源的)的时间,在这个时间到来之前,不会重复进行扫描的,这样也就大幅度降低了扫描的次数.

此处"提高效率"不是缩短执行时间(毕竟,定时器的时间是固定那么多的),这样做只不过是减少了资源的利用率,避免了不必要的CPU浪费.

import java.util.PriorityQueue;
import java.util.Timer;

/**
 * Created with IntelliJ IDEA
 * Description:
 * User: lenovo
 * Date: 2023 -10 -08
 * Time: 17:54
 */
//实现定时器
    class MyTimerTask implements Comparable<MyTimerTask>{
        //具体内容
    private Runnable runnable;
        //延时时间
    private long time;
    //构造方法
    public MyTimerTask(Runnable runnable,int time){
        this.runnable=runnable;
        this.time=System.currentTimeMillis()+time;
    }

    public Runnable getRunnable() {
        return runnable;
    }

    public long getTime() {
        return time;
    }

    @Override
    public int compareTo(MyTimerTask o) {
        //这里是怎么知道谁减谁的呢?很简单,不用记,去试一试,无非就是从小到大以及从大到小两种情况
        return (int)(this.time-o.time);
    }
}
    class MyTimer{
        //需要优先级队列
        //但是注意了,能使用优先级队列的前提是,优先级队列中的元素是可比较的
        PriorityQueue<MyTimerTask> priorityQueue=new PriorityQueue<>();
        //需要TimerTask
        //需要进行加锁
        private Object locker =new Object();
    //需要schedule方法
        public void schedule(Runnable runnable,int delay) {
            MyTimerTask myTimerTask = new MyTimerTask(runnable, delay);
            //在堆中存放元素
            synchronized (locker) {
                priorityQueue.offer(myTimerTask);
                locker.notify();
            }
        }
        //需要一个扫描线程
        public MyTimer(){

        Thread t=new Thread(()->{
            try {
                while(true){
            synchronized (locker){
                    while(priorityQueue.isEmpty()){
                        locker.wait();
                        //continue;
                    }
                    //当前时间
                    long curtime=System.currentTimeMillis();
                    MyTimerTask myTimerTask=priorityQueue.peek();
                    if(curtime>=myTimerTask.getTime()){
                        myTimerTask.getRunnable().run();
                        priorityQueue.poll();
                    }else{

                            //1.这里使用sleep是不合适的,因为sleep在休眠的时候并不会释放锁
                            //会影响其他线程进行schedule
                            //2.不能提前中断sleep,假如以前最早的任务是13.20,现在是13.00,sleep20分钟
                            //但是现在有一个紧急任务,需要13.05去执行,此时就无法去中断sleep
                            //也就是说,要使用一个可以增加一个任务就中断休眠重新判定的休眠
                            //此时wait  notify是最好的选择
                            //Thread.sleep(myTimerTask.getTime()-curtime);

                            //由于我们在这个线程中多次用到wait,所以我们直接让原本属于这里的try-catch
                            // 去包含整个线程的执行内容
                            locker.wait(myTimerTask.getTime()-curtime);
                    }
                }
            }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        t.start();
    }
    }

public class Demo22 {
    public static void main(String[] args) {
        MyTimer myTimer=new MyTimer();
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello 3");
            }
        },3000);
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello 2");
            }
        },2000);
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello 1");
            }
        },1000);
        System.out.println("程序开始");
    }
}

请一定要注意认真的去看代码中的注释!!!(我懒得挑出来总结了)

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值