四大经典案例_定时器及java代码实现

举报
bug郭 发表于 2022/09/30 23:38:26 2022/09/30
【摘要】 大家好,我是bug郭,一名双非科班的在校大学生。对C/JAVA、数据结构、Linux及MySql、算法等领域感兴趣,喜欢将所学知识写成博客记录下来。 希望该文章对你有所帮助!如果有错误请大佬们指正!共同学习交流作者简介:CSDN java领域新星创作者blog.csdn.net/bug…掘金LV3用户 juejin.cn/user/bug…阿里云社区专家博主,星级博主,developer.a...

大家好,我是bug郭,一名双非科班的在校大学生。对C/JAVA、数据结构、Linux及MySql、算法等领域感兴趣,喜欢将所学知识写成博客记录下来。 希望该文章对你有所帮助!如果有错误请大佬们指正!共同学习交流

作者简介:

定时器

定时器是什么

定时器也是软件开发中的一个重要组件. 类似于一个 “闹钟”. 达到一个设定的时间之后, 就执行某个指定好的代码.

也就是说定时器有像joinsleep等待功能,不过他们是基于系统内部的定时器,
而我们要学习的是在java给我们提供的定时器包装类,用于到了指定时间就执行代码!
并且定时器在我们日常开发中十分常用!

java给我们提供了专门一个定时器的封装类Timerjava.util包下!

Timer定时器

Timer类下有一个schedule方法,用于安排指定的任务和执行时间!
也就达到了定时的效果,如果时间到了,就会执行task!
在这里插入图片描述

  • schedule包含两个参数.
  • 第一个参数指定即将要执行的任务代码,
  • 第二个参数指定多长时间之后执行 (单位为毫秒).
//实例
import java.util.Timer;
import java.util.TimerTask;
public class Demo1 {
    public static void main(String[] args) {
        //在java.util.Timer包下
        Timer timer = new Timer();
        //timer.schedule()方法传入需要执行的任务和定时时间
        //Timer内部有专门的线程负责任务的注册,所以不需要start
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello Timer!");
            }
        },3000);
        //main线程
        System.out.println("hello main!");
    }
}

在这里插入图片描述
我们可以看到我们只需要创建一个Timer对象,然后调用schedule返回,传入你要执行的任务,和定时时间便可完成!

定时器实现

我们居然知道java中定时器的使用,那如何自己实现一个定时器呢!

我们可以通过Timer中的源码,然后进行操作!

Timer内部需要什么东西呢!

我们想想Timer的功能!
可以定时执行任务!(线程)
可以知道任务啥时候执行(时间)
可以将多个任务组织起来对比时间执行

  • 描述任务
    也就是schedule方法中传入的TimerTake
    创建一个专门表示定时器中的任务
class MyTask{
    //任务具体要干啥
    private Runnable runnable;
    //任务执行时间,时间戳
    private long time;
    ///delay是一个时间间隔
    public MyTask(Runnable runnable,long delay){
            this.runnable = runnable;
            time = System.currentTimeMillis()+delay;
    }
    public void run(){ //描述任务!
        runnable.run();
    }
}
  • 组织任务
    组织任务就是将上述的任务组织起来!
    我们知道我们的任务需要在多线程的环境下执行,所以就需要有线程安全,阻塞功能的数据结构!并且我们的任务到了时间就需要执行,也就是需要时刻对任务排序!
    所以我们采用PriorityBlockingQueue优先级队列!阻塞!
    在这里插入图片描述
    但是这里我们使用了优先级队列,我们需要指定比较规则,就是让MyTask实现Comparable接口,重写compareTo方法,指定升序排序,就是小根堆!
    在这里插入图片描述

  • 执行时间到了的任务
    我们可以创建一个线程,执行时间到了的任务!

//执行时间到了的任务!
    public MyTimer(){
        Thread thread = new Thread(()->{
           while (true){
               try {
                   MyTask task = queue.take();//获取到队首任务
                   //比较时间是否到了
                   //获取当前时间戳
                   long curTime = System.currentTimeMillis();
                   if(curTime<task.getTime()){//当前时间戳和该任务需要执行的时间比较
                       //还未到达执行时间
                       queue.put(task); //将任务放回
                   }else{//时间到了,执行任务
                       task.run();
                   }
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        });
        thread.start();//启动线程!
    }

//定时器完整代码
import java.util.concurrent.PriorityBlockingQueue;
class MyTask implements Comparable<MyTask>{
    //任务具体要干啥
    private Runnable runnable;

    public long getTime() {
        return time;
    }

    //任务执行时间,时间戳
    private long time;
    ///delay是一个时间间隔
    public MyTask(Runnable runnable,long delay){
            this.runnable = runnable;
            time = System.currentTimeMillis()+delay;
    }
    public void run(){ //描述任务!
        runnable.run();
    }
    @Override
    public int compareTo(MyTask o) {
        return (int)(this.time - o.time);
    }
}
public class MyTimer{
    //定时器内部需要存放多个任务
    private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
    public void schedule(Runnable runnable,long delay){
        MyTask task = new MyTask(runnable,delay);//接收一个任务!
        queue.put(task);//将任务组织起来
    }
   //执行时间到了的任务!
    public MyTimer(){
        Thread thread = new Thread(()->{
           while (true){
               try {
                   MyTask task = queue.take();//获取到队首任务
                   //比较时间是否到了
                   //获取当前时间戳
                   long curTime = System.currentTimeMillis();
                   if(curTime<task.getTime()){//当前时间戳和该任务需要执行的时间比较
                       //还未到达执行时间
                       queue.put(task); //将任务放回
                   }else{//时间到了,执行任务
                       task.run();
                   }
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        });
        thread.start();//启动线程!
    }
}
//测试
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("hello main");
    }

在这里插入图片描述
我们再来检查一下下面代码存在的问题!

 //执行时间到了的任务!
    public MyTimer(){
        Thread thread = new Thread(()->{
           while (true){
               try {
                   MyTask task = queue.take();//获取到队首任务
                   //比较时间是否到了
                   //获取当前时间戳
                   long curTime = System.currentTimeMillis();
                   if(curTime<task.getTime()){//当前时间戳和该任务需要执行的时间比较
                       //还未到达执行时间
                       queue.put(task); //将任务放回
                   }else{//时间到了,执行任务
                       task.run();
                   }
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        });
        thread.start();//启动线程!
    }
 

我们上述代码还存在一定缺陷就是执行线程到了的代码,我们的while循环一直在处于忙等状态!
就好比生活中:
你9点要去做核酸,然后你过一会就看时间,一会就看时间,感觉就有啥大病一样!
所以我们可以定一个闹钟,到了时间就去,没到时间可以干其他的事情!
此处的线程也是如此!我们这里也可以使用wait阻塞! 然后到了时间就唤醒,就解决了忙等问题!
我们的wait可以传入指定的时间,到了该时间就唤醒!!!

我们再思考另一个问题!

如果又加入了新的任务呢?
我们此时也需要唤醒一下线程,让线程重新拿到队首元素!

//最终定时器代码!!!!
import java.util.concurrent.PriorityBlockingQueue;
class MyTask implements Comparable<MyTask>{
    //任务具体要干啥
    private Runnable runnable;

    public long getTime() {
        return time;
    }
    //任务执行时间,时间戳
    private long time;
    ///delay是一个时间间隔
    public MyTask(Runnable runnable,long delay){
            this.runnable = runnable;
            time = System.currentTimeMillis()+delay;
    }
    public void run(){ //描述任务!
        runnable.run();
    }
    @Override
    public int compareTo(MyTask o) {
        return (int)(this.time - o.time);
    }
}
public class MyTimer{
    //定时器内部需要存放多个任务
    Object locker = new Object();//锁对象
    private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
    public void schedule(Runnable runnable,long delay){
        MyTask task = new MyTask(runnable,delay);//接收一个任务!
        queue.put(task);//将任务组织起来

        //每次拿到新的任务就需要唤醒线程,重新得到新的队首元素!
        synchronized (locker){
            locker.notify();
        }
    }
   //执行时间到了的任务!
    public MyTimer(){
        Thread thread = 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();
               }
           }
        });
        thread.start();//启动线程!
    }
}

总结:

  • 描述一个任务 runnable + time
  • 使用优先级队列组织任务PriorityBlockingQueue
  • 实现schedule方法来注册任务到队列
  • 创建扫描线程,获取队首元素,判断是否执行
  • 注意这里的忙等问题
//最后梳理一遍
import java.util.concurrent.PriorityBlockingQueue;
/**
 * Created with IntelliJ IDEA.
 * Description:定时器
 * User: hold on
 * Date: 2022-04-09
 * Time: 16:07
 */
//1.描述任务
class Task implements Comparable<Task>{
    //任务
    private Runnable runnable;
    //执行时间
    private long time;
    public Task(Runnable runnable,long delay){
        this.runnable = runnable;//传入任务
        //获取任务需要执行的时间戳
        time = System.currentTimeMillis() + delay;
    }
    @Override
    public int compareTo(Task o) {//指定比较方法!
        return (int) (this.time-o.time);
    }

    public long getTime() {//传出任务时间
        return time;
    }
    public void run(){
        runnable.run();
    }
}
//组织任务
class MyTimer1{
    private Object locker = new Object();//锁对象
    //用于组织任务
    private PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();
    public void schedule(Runnable runnable,long delay){
        Task task = new Task(runnable,delay);
        queue.put(task);//传入队列中
        synchronized (locker){
            locker.notify();//唤醒线程
        }
    }
    public MyTimer1(){
        //扫描线程获取队首元素,判断执行
        Thread thread = new Thread(()->{
           while (true){
               //获取当前时间戳
               long curTimer = System.currentTimeMillis();
               try {
                   Task task = queue.take();//队首元素出队
                   if(curTimer<task.getTime()){
                       //还未到达执行时间,返回队首元素
                       queue.put(task);
                       synchronized (locker){
                           //阻塞等待
                           locker.wait(task.getTime()-curTimer);
                       }
                   }else {
                       //执行任务
                       task.run();
                   }
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        });
        thread.start();//启动线程
    }
}
public class Demo2 {
    public static void main(String[] args) {
        MyTimer1 myTimer1 = new MyTimer1();
        myTimer1.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello Timer1");
            }
        },1000);

        System.out.println("hello main");
    }
}
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。