0
点赞
收藏
分享

微信扫一扫

Root mapping definition has unsupported parameters: [all : {analyzer=ik_max_wor

沈芏 21小时前 阅读 1
java-eejava

定时器及其模拟实现

一:定时器

定时器就是"闹钟"效果
指定一个任务(Runnable),并且指定一个时间(比如3000ms),此时这个任务不会立即执行,而是在时间到达之后,再去执行.
起到定时执行/延时执行的效果
定时器在日常开发中,是非常重要的基础组件.
比如:短信验证,验证码是有时效的,5min,
这样的效果就可以使用定时器,发送短信验证码的时候,生成验证码,保存起来,设定定时器,就会在5分钟之后,执行一个任务,删除刚才的那个验证码.
再比如:wait(),join(),带有阻塞时间,也可以理解成有定时器,定时器在达到时间之后自动唤醒.
正因为定时器非常重要,未来实际开发中,甚至会把定时器功能单独封装成服务器,供整个分布式系统来使用.

二:Timer

Java标准库的定时器.

public class Demo1 {
    public static void main(String[] args) {
        Timer timer=new Timer();
        timer.schedule(new TimerTask() {
            //TimerTask 相当于Runnable
            @Override
            public void run() {
                System.out.println("hello 5000");
            }
        },5000);//5000ms后执行
        /**
         * delay :表示"多长时间后执行,以当前执行"schedule"的时刻为基准
         * 继续等delay时间之后再去进一步的执行
         *
         */
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello 1000");
            }
        },1000);//1000ms后执行
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello 3000");
            }
        },3000);//3000ms后执行


    }
}

在这里插入图片描述

发现线程并没有结束,这是因为Timer内部包含了前台线程.阻止了进程结.

三:定时器的模拟实现

3.1:需求分析:

(1)能够延时执行任务/指定时间执行任务.
(2)能够管理多个任务

3.2:需要执行的任务

一个完整的任务,既要包含任务本身内容,也要 包含任务执行时间.
定义一个类,表示一个任务,有两个属性:任务内容和任务执行时间.
当前 自己定义的任务中,要保存执行任务的绝对时间(时间戳),为了后续线程执行的时候,可以方便的判定,该任务是否能够执行,如果是保存delay,随着时间的推移,还要更新delay,比较麻烦.

3.3:管理任务的方式

管理任务,最直观的方式就是通过List,通过遍历List集合中的元素,来判断是否执行当前线程,但如果这里的元素有很多,后续用来执行任务的线程,就需要不停循环的扫描这里的每个任务,分别判断是否要执行,效率比较低.
可以把这些任务通过优先级队列保存起来,按照时间作为优先级的先后的标准,就可以做到,队首元素就是时间最靠前的任务.
优先级队列,引用类型需要知道比较方法,此时需实现Compable接口.
扫描线程只需要关注队首元素即可(时间最靠前的任务,没到执行时间,剩下的 元素就更没到执行时间).

3.4:负责执行任务的线程

在构造方法中,创建一个线程,用线程来执行队列中的元素.

class MyTimerTask{
    private Runnable runnable;//任务
    private long time;//执行任务的时间
    public MyTimerTask(Runnable runnable,long delay){
        //传入的是延时执行时间,换算一下,变成绝对时间(时间戳)
        this.time = System.currentTimeMillis()+delay;
        this.runnable = runnable;
    }
    public long getTime(){
        return  time;
    }
    public void run(){
        runnable.run();//执行任务
    }
    
}
class MyTimer{
    
    PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();//使用优先级队列,管理多个任务
    public MyTimer(){
        
        Thread t=new Thread(()->{
            while(true){
                if(queue.size()==0){
                     continue;
                }
              MyTimerTask task = queue.peek();
             long curTime = System.currentTimeMillis();
             if(curTime>= task.getTime()){
                 task.run();
                queue.poll();//执行完之后将该任务从任务队列中删除
             }else{
               continue;
                
            }
        });
        t.start();
        
    }
    public void schedule(Runnable runnable,long delay){
        MyTimerTask myTimerTask=new MyTimerTask(runnable,delay);
        queue.offer(myTimerTask);
    }
}
public class Demo2 {
    public static void main(String[] args) {
        MyTimer myTimer=new MyTimer();
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello 5000");
            }
        },5000);

    }
}

在这里插入图片描述线程安全改进:加锁;

在这里插入图片描述阻塞的时候,使用sleep行不行???
当然是不行的.
因为当有新的任务来了的时候,如果该任务执行时间比原先队列中最早要执行的任务还早,此时就需要唤醒阻塞的线程.
比如,现在13:00,老板1给我安排活,14:00开始执行,那么,我可以等待60min,但老板2给吗安排工作,13:30,开始执行,那么我只可以等待30min了,但到13:30就必须立即执行,所以需要通知我,不能让我阻塞等待60min.
所以,在有新的任务来的时候,需要唤醒.

sleep是睡着了,只有使用interrupt方法唤醒sleep,但这属于非常规的方法,一般在处理异常业务的时候使用.
notify唤醒wait,属于常规编程手法,一般用于处理正常业务.
除了上述的区别外,还有很重要的区别,那就是
wait休眠期间会释放锁,
sleep是报着锁睡,其他任务入队列的时候,就会阻塞等待,尤其是队列为空时,就会一直sleep,那么入队操作将一直阻塞等待.

整体代码实现:

class MyTimerTask implements Comparable<MyTimerTask>{
    private Runnable runnable;//任务
    private long time;//执行任务的时间
    public MyTimerTask(Runnable runnable,long delay){
        //传入的是延时执行时间,换算一下,变成绝对时间(时间戳)
        this.time = System.currentTimeMillis()+delay;
        this.runnable = runnable;
    }
    public long getTime(){
        return  time;
    }
    public void run(){
        runnable.run();//执行任务
    }

    @Override
    public int compareTo(MyTimerTask o) {
        return (int)(this.time-o.time);
    }
}
class MyTimer{
        Object locker=new Object();
    PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();//使用优先级队列,管理多个任务
    public MyTimer(){

        Thread t=new Thread(()->{
            synchronized (locker){
                while(true){
                    if(queue.size()==0){
                        try {
                            locker.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    MyTimerTask task = queue.peek();
                    long curTime = System.currentTimeMillis();
                    if(curTime>= task.getTime()){
                        task.run();
                        queue.poll();//执行完之后将该任务从任务队列中删除
                    }else{
                        try {
                            locker.wait(task.getTime()-curTime);//阻塞时间
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                }
            }

        });
        t.start();

    }
    public void schedule(Runnable runnable,long delay){
        MyTimerTask myTimerTask=new MyTimerTask(runnable,delay);
        synchronized (locker){
            queue.offer(myTimerTask);
            locker.notify();
        }

    }

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

        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello 3000");
            }
        },3000);

    }
}
举报

相关推荐

0 条评论