多线程代码案例之定时器

目录

1.介绍定时器

2.使用定时器

3.模拟实现定时器

1.介绍定时器

1.1.定时器概念

(1)“定时器”类似闹钟,在指定的时间会触发一定的操作

(2)多线程中的定时器,是通过指定一个任务(Runnable),并且加上一个时间(例如:3000ms);这个任务不会立即被执行,而是会等待3000ms后才会开始执行

(3)上面这种效果称为:延时执行/定时执行

1.2.定时器使用案例

在平时生活中,有许多案例都是运用这种思想,比如验证码、某些链接等等,下面我们介绍短信验证码。

(1)向我们平时遇到的验证码有效时间,都是60s;当超过了这个时间,才输入验证码,就会发生失效。

(2)在发生短信的时候,生成一个验证码并且保存起来,并且设置一个时间(60s),在60s之后,就会执行一个:删除该验证码 的任务,此时,验证码就无效了。

而我们标准库中,也有可供我们使用的定时器。

2.使用定时器

2.1.认识定时器的使用

(1)定时器的类是:Timer

 (2)实例化一个定时器对象

Timer timer = new Timer();

这就是实例化好了一个定时器对象。

(3)定时器中的方法

方面名如下 

schedule();

这个方法就是用来向定时器中添加任务和时间,当超过那么多时间之后,定时器中的线程就会执行该任务。

参数介绍:

这就是关于定时器最基本的认识了,下面介绍一下如何使用。

2.2.使用定时器

(1)实例化定时器对象

Timer timer = new Timer();

(2)向任务方法添加任务

public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("我是Runnable的子类,5000");
            }
        },5000);
}

这样就完成了任务和时间的设置,看一下运行结果:

效果也是非常的ok啊,接下来添加三个不同的时间的任务,看一下效果。

(3)不同时刻的任务

 public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("我是Runnable的子类,5000");
            }
        },5000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("我是Runnable的子类,3000");
            }
        },3000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("我是Runnable的子类,1000");
            }
        },1000);
    }

运行结果:

总结一下使用定时器的步骤:1)实例化一个定时器对象 2)调用定时器对象中的方法,向里面添加任务和指定的时间。到达一定的时间就会自动执行该任务。

3.模拟实现定时器

上述我们使用了标准库中的定时器,现在,我们要自己模拟实现一个定时器。‘

 实现定时器需要的东西:(1)可以延时执行任务(2)可以管理多个任务

得到一个大致的框架:

执行任务-->线程。管理多个任务-->使用数据结构。延时执行任务-->不断循环

我们需要模拟实现Timer。有构造方法、schedule方法

 基本步骤也就是下面的两步:

(1)任务类

我们知道系统提供的定时器,是可以将任务提交进去,到达规定的时间内就会执行,所以,我们模拟实现也需要可以保存任务的数据结构。

第一、我们想到使用任务队列,也就是阻塞队列

第二、使用优先级队列

这样一对比,虽然阻塞队列看起来线程安全,但是不一定好。我们选择优先级队列,原因是它本身带有优先级,也就是可以让时间少的任务先被执行,也就可以完成按时间顺序执行的效果;至于线程安全问题,我们加锁就好了。

下面就是一个任务类:

class MyTimerTask {//这个类用来保存任务和时间
    private Runnable runnable;
    private long time;//这里的时间和delay不一样,这里的是到达某个时间点就开始执行任务,而后者是等过了多少分钟后

    public MyTimerTask(Runnable runnable,long time) {
        this.runnable = runnable;
        this.time = System.currentTimeMillis() + time;//需要换算一下时间
    }
    public void run() {
        //用于执行任务
        runnable.run();
    }
    public long getTime() {
        return time;//返回时间。例如:14:00
    }
}

要想让它可以按照时间的顺序执行,就需要这个任务可以记录时间,也就是带有时间属性。但是这个任务类是我们自己定义的,直接存入优先级队列中是不行的,所以我们要实现Comparator接口,让其可以比较。

class MyTimerTask implements Comparable<MyTimerTask>{//这个类用来保存任务和时间
    private Runnable runnable;
    private long time;//这里的时间和delay不一样,这里的是到达某个时间点就开始执行任务,而后者是等过了多少分钟后

    public MyTimerTask(Runnable runnable,long time) {
        this.runnable = runnable;
        this.time = System.currentTimeMillis() + time;//需要换算一下时间
    }
    public void run() {
        //用于执行任务
        runnable.run();
    }
    public long getTime() {
        return time;//返回时间。例如:14:00
    }

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

这就是任务类的实现

(2)Timer类

这个类的表明:实例化Timer对象,并且调用该对象中的方法,将任务和时间提交进去。

实质作用:可以接收任务,也就是可以保存任务;可以执行任务,也就要有线程

下面就是一个初步的框架:

class MyTimer {
    //保存任务
    private PriorityQueue<MyTimerTask> priorityQueue = new PriorityQueue<>();
    //构造方法中内置线程,用于执行任务
    public MyTimer() {
        Thread t = new Thread(()->{
           try {
               while (true) {//线程不断的执行
                       if(priorityQueue.size() == 0) {
                           continue;//暂时没有任务,进入下一轮循
                       }
                       MyTimerTask task = priorityQueue.peek();//取出堆顶对象
                       long Time = task.getTime();//获取该任务需要执行的时间点,比如14:00
                       if(System.currentTimeMillis() >= Time) {//到达时间就执行
                           //比如现在的时间是14:20,就会进入if
                           task.run();//执行任务
                           priorityQueue.poll();
                       }
               }
           }catch (InterruptedException e) {
               e.printStackTrace();
           }
        });
        t.start();
    }
//用于添加任务
    public void schedule (Runnable runnable,long time) {
           MyTimerTask task = new MyTimerTask(runnable,time);
           priorityQueue.offer(task);//将任务对象保存到优先级队列中
       }
    }

}

这样的定时器也是可以使用的,但是存在两个问题:第一个就是线程安全问题,第二个就是当任务未到时间时,cpu一直处于空转的状态(也就是while循环一直在进行),所以我们需要进行改进。

所以我们需要引入锁和线程等待机制

class MyTimer {
    private PriorityQueue<MyTimerTask> priorityQueue = new PriorityQueue<>();//保存任务
    private Object locker = new Object();

    public MyTimer() {//构造方法、一创建就开始等待执行线程
        Thread t = new Thread(()->{
           try {
               while (true) {//线程不断的执行
                   synchronized (locker) {//对 操作队列的操作进行加锁
                       if(priorityQueue.size() == 0) {
                           //continue;//暂时没有任务,进入下一轮循环
                           locker.wait();//没有任务时,阻塞等待
                       }
                       MyTimerTask task = priorityQueue.peek();//取出堆顶对象
                       long Time = task.getTime();//获取该任务需要执行的时间点,比如14:00
                       if(System.currentTimeMillis() >= Time) {//到达时间就执行
                           //比如现在的时间是14:20,就会进入if
                           task.run();//执行任务
                           priorityQueue.poll();
                       }else {
                           locker.wait(Time - System.currentTimeMillis());//等待指定的时间,否则死循环
                       }
                   }
               }
           }catch (InterruptedException e) {
               e.printStackTrace();
           }
        });
        t.start();
    }
    public void schedule (Runnable runnable,long time) {//用于添加任务
       synchronized (locker) {//对操作对象的进行加锁
           MyTimerTask task = new MyTimerTask(runnable,time);//每次的任务和时间就创建一个对象
           priorityQueue.offer(task);//将任务对象保存到优先级队列中
           locker.notify();//当有新任务进来时,就唤醒wait
       }
    }

}

这就是完整的代码,我们配合上述的任务类,允许一下代码:

测试类:

代码运行结果:

嗯,很完美!

  • 37
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码小娥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值