模拟实现定时器

前言

定时器的功能,就是一个类似于闹钟的到点运行的功能。

 

目录

前言

一、Timer 类

二、分析 Timer 类

三、完整代码实现

结语


一、Timer 类

Java库提供 Timer 类,实现 schedule 方法,给方法提供任务和时间,到时间就运行任务,如:

import java.util.Timer;
import java.util.TimerTask;

public class Main {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("111");
            }
        },1000);
    }
}

运行截图:

当当前任务执行完毕之后,线程 timer 是不会结束的, 会一直阻塞等待,直到下一个任务的录入。

二、分析 Timer 类

1)定时器是将多个任务按照时间先后执行,由此一个任务具有任务的具体内容,和执行时间:

class TimerTake{
    private long time;
    private Runnable runnable;

    public TimerTake(long time, Runnable runnable) {
        this.time = time+System.currentTimeMillis();//当前时间+等待时间
        this.runnable = runnable;
    }

    public long getTime() {
        return time;
    }

    public Runnable getRunnable() {
        return runnable;
    }
}

2)执行顺序是按照时间先后,故该TimerTake 类应该带有比较器:

class TimerTake implements Comparable<TimerTake>{
    private long time;
    private Runnable runnable;

    public TimerTake(long time, Runnable runnable) {
        this.time = time;
        this.runnable = runnable;
    }

    public long getTime() {
        return time;
    }

    public Runnable getRunnable() {
        return runnable;
    }

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

3)为了更好的实现有一个任务实现一个任务,没有任务等待下一个任务的添加,这里组织任务的数据结构使用阻塞队列(阻塞队列_线程安全版本_生产消费者模型_Y君的进化史的博客-CSDN博客),不过考虑到时间先后顺序,采用阻塞优先级队列更加合理再写一个类MyTimer组织TimerTake类任务的添加在schedule方法中实现

class MyTimer{
    private PriorityBlockingQueue<TimerTake> queue = new PriorityBlockingQueue<TimerTake>();

    public void schedule(Runnable runnable,long time){
        TimerTake timerTake = new TimerTake(runnable,time);
        queue.put(timerTake);
    }
}

4)需要在一调用schedule时,按照时间执行任务,说明在MyTimer构造方法中应该存在线程的调用,使其一直运行:

    class Timer extends Thread {
        @Override
        public void run() {
            while(true){
                if(queue.isEmpty()){
                    //空队列,没有任务
                }
                try {
                    TimerTake timerTake = queue.take();
                    long time = System.currentTimeMillis();//当前时间
                    if(time >= timerTake.getTime()){
                        //执行任务
                        timerTake.getRunnable().run();
                    }else{
                        //将任务添加回去
                        queue.put(timerTake);
                    }
                } catch (InterruptedException e) {
                e.printStackTrace();
                }
            }
        }
    }

    MyTimer(){
        Timer timer = new Timer();
        timer.start();
    }

5)上述代码确实可以处理任务的执行问题,但是一直使用while和if判断会消耗大量的CPU资源,所以选择使用休眠等待时间,有两个选择sleep 和 wait-notify。使用sleep会影响任务的录入,故选择wait-notify:

6)那什么时候使用notify唤醒呢?应该在任务加入的时候使用:

    public void schedule(Runnable runnable,long time){
        TimerTake timerTake = new TimerTake(runnable,time);
        queue.put(timerTake);
        synchronized (object){
            object.notify();
        }
    }

7)队列为空时,采取wait-notify等待-唤醒机制,等任务添加时唤醒:

8)考虑到线程安全问题,整个代码再加上synchronized 和 volatile:



三、完整代码实现

下面代码是Timer的模拟实现:


import java.util.concurrent.PriorityBlockingQueue;


class TimerTake implements Comparable<TimerTake>{
    private long time;
    private Runnable runnable;

    public TimerTake( Runnable runnable,long time) {
        this.time = time+System.currentTimeMillis();//当前时间+等待时间
        this.runnable = runnable;
    }

    public long getTime() {
        return time;
    }

    public Runnable getRunnable() {
        return runnable;
    }

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

class MyTimer {
    volatile private PriorityBlockingQueue<TimerTake> queue = new PriorityBlockingQueue<TimerTake>();

    public void schedule(Runnable runnable, long time) {
        synchronized (object) {
            TimerTake timerTake = new TimerTake(runnable, time);
            queue.put(timerTake);
            object.notify();
        }
    }

    private static Object object = new Object();

    class Timer extends Thread {
        @Override
        public void run() {
            while (true) {
                try {
                    synchronized (object) {
                        while (queue.isEmpty()) {
                            //空队列,没有任务
                            object.wait();
                        }
                        TimerTake timerTake = queue.take();
                        long time = System.currentTimeMillis();//当前时间
                        if (time >= timerTake.getTime()) {
                            //执行任务
                            timerTake.getRunnable().run();
                        } else {
                            //将任务添加回去
                            queue.put(timerTake);

                            object.wait(timerTake.getTime() - time);

                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    MyTimer(){
        Timer timer = new Timer();
        timer.start();
    }
}

下面代码是博主通过main方法调用这个模拟的MyTimer实现几个小功能:

public class Main {
    public static void main(String[] args) {
        MyTimer myTimer = new MyTimer();
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("111");
            }
        },1000);
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("333");
            }
        },3000);
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("222");
            }
        },2000);
    }
}

运行截图是:


结语

这篇博客如果对你有帮助,给博主一个免费的点赞以示鼓励,欢迎各位🔎点赞👍评论收藏⭐,谢谢!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/72230.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++ 循环

有的时候&#xff0c;可能需要多次执行同一块代码。一般情况下&#xff0c;语句是顺序执行的&#xff1a;函数中的第一个语句先执行&#xff0c;接着是第二个语句&#xff0c;依此类推。 编程语言提供了允许更为复杂的执行路径的多种控制结构。 循环语句允许我们多次执行一个…

设计模式行为型——访问者模式

目录 访问者模式的定义 访问者模式的实现 访问者模式角色 访问者模式类图 访问者模式举例 访问者模式代码实现 访问者模式的特点 优点 缺点 使用场景 注意事项 实际应用 访问者模式的定义 访问者模式&#xff08;Visitor Pattern&#xff09;属于行为型设计模式&am…

docker 安装mongodb 虚拟机安装mongodb

生产环境直接安装比较好&#xff0c;以及使用集群环境&#xff0c;本文仅测试交流使用&#xff0c;我用来写分布式im测试使用&#xff1a; nami-im: 分布式im, 集群 zookeeper netty kafka nacos rpc主要为gate&#xff08;长连接服务&#xff09; logic &#xff08;业务&…

Java-运算符和控制语句(下)(基于c语言的补充)

输出到控制台 System.out.println(msg); // 输出一个字符串, 带换行 System.out.print(msg); // 输出一个字符串, 不带换行 System.out.printf(format, msg); // 格式化输出 从键盘输入 使用 Scanner 读取字符串/整数/浮点数 首先需要导入util包 自动导入util包 这里把回车看…

企业服务器数据库遭到malox勒索病毒攻击后如何解决,勒索病毒解密

网络技术的发展不仅为企业带来了更高的效率&#xff0c;还为企业带来信息安全威胁&#xff0c;其中较为常见的就是勒索病毒攻击。近期&#xff0c;我们公司收到很多企业的求助&#xff0c;企业的服务器数据库遭到了malox勒索病毒攻击&#xff0c;导致系统内部的许多重要数据被加…

简单线性回归:预测事物间简单关系的利器

文章目录 &#x1f340;简介&#x1f340;什么是简单线性回归&#xff1f;&#x1f340;简单线性回归的应用场景使用步骤&#xff1a;注意事项&#xff1a; &#x1f340;代码演示&#x1f340;结论 &#x1f340;简介 在数据科学领域&#xff0c;线性回归是一种基本而强大的统…

Grounding dino + segment anything + stable diffusion 实现图片编辑

目录 总体介绍总体流程 模块介绍目标检测&#xff1a; grounding dino目标分割&#xff1a;Segment Anything Model (SAM)整体思路模型结构&#xff1a;数据引擎 图片绘制 集成样例 其他问题附录 总体介绍 总体流程 本方案用到了三个步骤&#xff0c;按顺序依次为&#xff1a…

前端处理后端返回的数据中有\n\n字样的换行符标识

后端返回的数据&#xff1a; 上面圈着的部分就是\n&#xff0c;前端需要将数据进行换行&#xff0c;对于这类型的数据&#xff0c;在前端页面是需要进行稍微处理才能正常显示。如果没有经过处理&#xff0c;那么内容是不会在有换行符的位置进行换行显示的 解决办法1&#xff1…

微服务07-分布式缓存

前提: 单机的Redis存在四大问题: 解决办法:基于Redis集群解决单机Redis存在的问题 1、Redis持久化 Redis 具有持久化功能,其会按照设置以 快照 或 操作日志 的形式将数据持久化到磁盘。 Redis有两种持久化方案: RDB持久化AOF持久化注意: RDB 是默认持久化方式,但 Red…

【MySQL】并发执行事务可能存在的问题, 事务的四种隔离级别

文章目录 前言一、并发执行事务可能存在的问题1, 脏读问题2, 不可重复读3, 幻读 二、MySQL 的四种隔离级别1, READ UNCOMMITTED 读未提交2, READ COMMITTED 读已提交3, REPEATABLE READ 可重复读 (MySQL 的默认事务隔离级别)4, SERIALIZABLE 串行化 总结 前言 各位读者好, 我是…

Easy Rules规则引擎(1-基础篇)

目录 一、序言二、Easy Rules介绍三、定义规则(Rules)1、规则介绍2、编程式规则定义3、声明式规则定义 四、定义事实(Facts)五、定义规则引擎(Rules Engine)1、规则引擎介绍2、InferenceRulesEngine规则引擎示例(1) 定义触发条件(2) 定义规则触发后的执行行为(3) 测试用例 一、…

【密码学】密码棒密码

密码棒密码 大约在公元前700年,古希腊军队使用一种叫做scytale的圆木棍来进行保密通信。其使用方法是这样的:把长带子状羊皮纸缠绕在圆木棍上,然后在上面写字;解下羊皮纸后,上面只有杂乱无章的字符,只有再次以同样的方式缠绕到同样粗细的棍子上,才能看出所写的内容。快速且不容…

安卓源码分析(10)Lifecycle实现组件生命周期管理

参考&#xff1a; https://developer.android.google.cn/topic/libraries/architecture/lifecycle?hlzh-cn#java https://developer.android.google.cn/reference/androidx/lifecycle/Lifecycle 文章目录 1、概述2、LifeCycle类3、LifecycleOwner类4、LifecycleObserver类 1、…

聊聊51单片机

目录 1.介绍 2.发展 3.应用领域 4.发展前景 1.介绍 51单片机&#xff08;AT89C51&#xff09;是一种常见的8位微控制器&#xff0c;属于Intel MCS-51系列。它是一种低功耗、高性能的单片机&#xff0c;广泛应用于嵌入式系统中。 51单片机具有很多特点和功能&#xff0c;例如…

智慧城市美术效果Unity实现笔记流程

智慧城市美术效果Unity实现笔记流程&#xff1a; 参考 对标 效果图&#xff1a; 参考资料&#xff1a; 方案一&#xff1a; fBlender GIS 获取城市 房屋道路等数据 安装BlenderGIS插件 落叶大师智慧城市效果解析 方案二&#xff1a; CityEngine2022地块生成 写实类-参考图&…

棒球在国际上的流行·棒球1号位

棒球在国际上的流行 1. 棒球的起源与历史 棒球的起源源于美国。19世纪中叶&#xff0c;由于美国领土的扩张&#xff0c;当时的美国殖民地的印第安人将棒球类游戏&#xff0c;带到了当时的弗吉尼亚州的奥克兰。后来&#xff0c;棒球运动流传到了加利福尼亚州的圣迭戈。早期的棒…

初识鸿蒙跨平台开发框架ArkUI-X

HarmonyOS是一款面向万物互联时代的、全新的分布式操作系统。在传统的单设备系统能力基础上&#xff0c;HarmonyOS提出了基于同一套系统能力、适配多种终端形态的分布式理念&#xff0c;能够支持手机、平板、智能穿戴、智慧屏、车机等多种终端设备&#xff0c;提供全场景&#…

adb用法,安卓的用户CA证书放到系统CA证书下

设备需root&#xff01;&#xff01;设备需root&#xff01;&#xff01;设备需root&#xff01;&#xff01; ​​​​​​​测试环境&#xff1a;redmi 5 plus、miui10 9.9.2dev&#xff08;安卓8.1&#xff09;、已root win下安装手机USB驱动&#xff08;过程略&#xff0c…

OPENCV C++(十二)模板匹配

正常模板匹配函数 matchTemplate(img, templatee, resultMat, 0);//模板匹配 这里0代表的是方法&#xff0c;一般默认为0就ok img是输入图像 templatee是模板 resultmat是输出 1、cv::TM_SQDIFF&#xff1a;该方法使用平方差进行匹配&#xff0c;因此最佳的匹配结果在结果为…