定时器--JAVA

news2024/10/7 14:30:59

定时器是软件开发中的一个重要组件,类似于一个"闹钟"当达到一个设定的时间之后,就执行某个指定好的代码(任务)。

Timer

JAVA标准库中已经为我们实现了一个定时器,我们直接new就行了。

Timer timer = new Timer();

Timer类中最重要的一个方法就是schedule(),这个方法用于设置定时器待执行的任务和执行任务的时间。

public static void main(String[] args) {
    Timer timer = new Timer();
    //在3秒后打印3000
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println("3000");
        }
    }, 3000);
    //在2秒后打印2000
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println("2000");
        }
    }, 2000);
    //在1秒后打印1000
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println("1000");
        }
    }, 1000);
    
    System.out.println("OK");
}

可以发现在代码执行完毕后程序并没有结束,这是因为虽然主线程结束了但是Timer类中的线程在阻止程序结束,它们还在等待新的任务进来被执行。

为了可以更好的理解定时器的原理,下面进行简单的模拟实现。

模拟实现

首先我们需要先创建一个MyTimerTask类,该类主要用来保存待执行的任务,和执行该任务的时间点。

class MyTimerTask {
    public Runnable runnable;
    //存储绝对时间,后期直接和当前时间比较大小就行
    public long time;

    public MyTimerTask(Runnable runnable, long time){
        this.runnable = runnable;
        this.time = time + System.currentTimeMillis();
    }
}

接着写我们的定时器类MyTimer。首先我们应该先选择一种数据结构来存储所有的MyTimerTask,这里我推荐使用优先级队列,也就是堆。因为如果使用数组或链表来存储就需要你不断地遍历该数组/链表,而使用优先级队列就可以只检查队首元素是否到执行时间。

public class MyTimer {
    private final PriorityQueue<MyTimerTask> priorityQueue = new PriorityQueue<>();
}

因为我们使用了优先级队列所以我们需要让MyTimerTask类可以进行比较。

class MyTimerTask implements Comparable<MyTimerTask>{
    ……
    @Override
    public int compareTo(MyTimerTask o) {
        return (int) (this.time-o.time);
    }
}

接着我们需要实现一个schedule方法可以接收定时任务和时间,因为是多线程代码所以我们还应该加一个属性用来充当锁对象。

public class MyTimer {
    private final PriorityQueue<MyTimerTask> priorityQueue = new PriorityQueue<>();
    private Object lock = new Object();

    public void schedule(Runnable runnable, long time) {
        synchronized (lock) {
            priorityQueue.add(new MyTimerTask(runnable, time));
        }
    }
}

此时我们还需要一个自动检查队列的线程,而且该线程并不需要程序员来启动和创建,所以我们可以将它写在构造方法中。

public MyTimer() {
    Thread t = new Thread(()->{
        while(true) {
            synchronized (lock) {
                if (priorityQueue.isEmpty()) {
                    //堆为空           
                }
                MyTimerTask myTimerTask = priorityQueue.peek();
                if (myTimerTask.time <= System.currentTimeMillis()) {
                    //执行当前任务
                    myTimerTask.runnable.run();
                    //将当前任务移除
                    priorityQueue.poll();
                }else {
                    //时间没到
                }
            };
        }
    });
    t.start();
}

此处我们可以用阻塞队列的思想,当堆为空或者执行时间没到就使用wait()进行等待。有人添加任务时就使用notify()进行唤醒线程。

public class MyTimer {
    private final PriorityQueue<MyTimerTask> priorityQueue = new PriorityQueue<>();
    private Object lock = new Object();

    public void schedule(Runnable runnable, long time) {
        synchronized (lock) {
            priorityQueue.add(new MyTimerTask(runnable, time));
            lock.notify();
        }
    }

    public MyTimer() {
        Thread t = new Thread(()->{
            while(true) {
                synchronized (lock) {
                    if (priorityQueue.isEmpty()) {
                        //如果堆为空就阻塞等待
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    MyTimerTask myTimerTask = priorityQueue.peek();
                    if (myTimerTask.time <= System.currentTimeMillis()) {
                        //执行当前任务
                        myTimerTask.runnable.run();
                        priorityQueue.poll();
                    }else {
                        //时间没到就阻塞等待
                        try {
                            lock.wait(myTimerTask.time-System.currentTimeMillis());
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                };
            }
        });
        t.start();
    }
}

简单测试

public static void main(String[] args) {
    MyTimer timer = new MyTimer();
    //在3秒后打印3000
    timer.schedule(()->{
            System.out.println("3000");
    }, 3000);
    //在2秒后打印2000
    timer.schedule(()->{
            System.out.println("2000");
    }, 2000);
    //在1秒后打印1000
    timer.schedule(()->{
            System.out.println("1000");
    }, 1000);

    System.out.println("OK");
}

完整代码

import java.util.PriorityQueue;

class MyTimerTask implements Comparable<MyTimerTask>{
    public Runnable runnable;
    //存储绝对时间,后期直接和当前时间比较大小就行
    public long time;

    public MyTimerTask(Runnable runnable, long time){
        this.runnable = runnable;
        this.time = time + System.currentTimeMillis();
    }

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

public class MyTimer {
    private final PriorityQueue<MyTimerTask> priorityQueue = new PriorityQueue<>();
    private Object lock = new Object();

    public void schedule(Runnable runnable, long time) {
        synchronized (lock) {
            priorityQueue.add(new MyTimerTask(runnable, time));
            lock.notify();
        }
    }

    public MyTimer() {
        Thread t = new Thread(()->{
            while(true) {
                synchronized (lock) {
                    if (priorityQueue.isEmpty()) {
                        //如果堆为空就阻塞等待
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    MyTimerTask myTimerTask = priorityQueue.peek();
                    if (myTimerTask.time <= System.currentTimeMillis()) {
                        //执行当前任务
                        myTimerTask.runnable.run();
                        priorityQueue.poll();
                    }else {
                        //时间没到就阻塞等待
                        try {
                            lock.wait(myTimerTask.time-System.currentTimeMillis());
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                };
            }
        });
        t.start();
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Fine-tuning:个性化AI的妙术

随着人工智能&#xff08;AI&#xff09;技术的迅猛发展&#xff0c;Fine-tuning作为一项重要而神奇的技术崭露头角。Fine-tuning俗称“微调技术。其本质上是对已有模型进行能力的迁移学习扩展&#xff0c;由于重新训练神经网络模型的成本太高&#xff0c;所以使用微调技术可以…

GZ075 云计算应用赛题第8套

2023年全国职业院校技能大赛&#xff08;高职组&#xff09; “云计算应用”赛项赛卷8 某企业根据自身业务需求&#xff0c;实施数字化转型&#xff0c;规划和建设数字化平台&#xff0c;平台聚焦“DevOps开发运维一体化”和“数据驱动产品开发”&#xff0c;拟采用开源OpenSt…

MySQL——深入数据库原理(事务及锁)

文章目录 锁行级锁共享 (S) 锁排他 (X) 锁间隙锁 表级锁意向锁自增锁Lock Table/DDL 事务ACID 原则1. 原子性 A2. 一致性 C3. 隔离性 I4. 持久性 D 隔离级别1. READ UNCOMMITTED&#xff08;未提交读&#xff09;2. READ COMMITTED&#xff08;提交读&#xff09;3. REPEATABLE…

强化学习应用(五):基于Q-learning的物流配送路径规划研究(提供Python代码)

一、Q-learning算法简介 Q-learning是一种强化学习算法&#xff0c;用于解决基于马尔可夫决策过程&#xff08;MDP&#xff09;的问题。它通过学习一个值函数来指导智能体在环境中做出决策&#xff0c;以最大化累积奖励。 Q-learning算法的核心思想是使用一个Q值函数来估计每…

《计算思维导论》笔记:10.2 什么是数据库与数据库系统?

《大学计算机—计算思维导论》&#xff08;战德臣 哈尔滨工业大学&#xff09; 《10.2 什么是数据库与数据库系统&#xff1f;》 数据库 简单来讲&#xff0c;数据库就是相互有关联关系的数据的集合。 一个表聚集了具有相同结构类型的若干个对象一行数据反映了某一对象的相关…

【Python数据分析系列】实现txt文件与列表(list)相互读写转换(源码+案例)

这是Python数据分析系列原创文章&#xff0c;我的第199篇原创文章。 一、问题 平时在做数据分析或者程序开发的时候&#xff0c;需要将中间的一些结果或最后的处理结果保存下来&#xff0c;比如保存为txt格式的文本文件&#xff0c;这就涉及列表与txt之间的一种读取和写入操作…

【python】11.文件和异常

文件和异常 实际开发中常常会遇到对数据进行持久化操作的场景&#xff0c;而实现数据持久化最直接简单的方式就是将数据保存到文件中。说到“文件”这个词&#xff0c;可能需要先科普一下关于文件系统的知识&#xff0c;但是这里我们并不浪费笔墨介绍这个概念&#xff0c;请大…

《WebKit 技术内幕》之一: 浏览器和浏览器内核

第一章 浏览器和浏览器内核 浏览器的内核是浏览器的最核心的部件。 1.浏览器 1.1 浏览器发展简介 80年代后期90年代初期&#xff1a;由Berners-Lee 发明&#xff0c;诞生了世界上第一个浏览器 WorldWideWeb&#xff0c;后改名为 Nexus&#xff1b;并于1991年公布源代码&…

《2023年终总结》

笔者来回顾一下2023年的个人成长。 2023年总的来说&#xff0c;工作和生活都相对比较顺利。 工作上领导给予了肯定的评价&#xff0c;升职加薪&#xff0c;对我的鼓舞很大&#xff1b; 生活上和女朋友的感情越来越好&#xff0c;生气频率降低&#xff0c;也能相互理解&#xf…

【编码魔法师系列_构建型4】原型模式(Prototype Pattern)

学会设计模式&#xff0c;你就可以像拥有魔法一样&#xff0c;在开发过程中解决一些复杂的问题。设计模式是由经验丰富的开发者们&#xff08;GoF&#xff09;凝聚出来的最佳实践&#xff0c;可以提高代码的可读性、可维护性和可重用性&#xff0c;从而让我们的开发效率更高。通…

Meproc:简单高效的跨平台进程/任务管理工具

最近使用 Melang 语言写了一个 supervisor 相似服务Meproc来管理进程。 Meproc 有如下特性&#xff1a; 使用 HTTP API 管理控制 Meproc 来管理进程跨平台&#xff0c;支持 UNIX/Linux 、Mac 、Windows 等平台支持 cron 类定时调度任务支持简单的任务间依赖关系支持原生的协…

基于51单片机的模拟量输入输出通道实验

实验一 模拟量输入输出通道实验&#xff08;C51&#xff09; 一、实验目的&#xff1a; 1、了解A/D、D/A转换的基本原理。 2、了解A/D转换芯片ADC0809、D/A转换芯片DAC0832的性能及编程方法。 3、掌握过程通道中A/D转换与D/A转换与计算机的接口方法。 4、了解计算机如何进…

第二证券:利空因素影响成本端 豆粕期现价偏弱运行

上个买卖周&#xff0c;受利空要素影响&#xff0c;内盘豆粕期价刷新2021年12月14日以来收盘价新低。到上周五收盘&#xff0c;豆粕主力合约MO2405最低下探至3075元/吨&#xff0c;收报3078元/吨&#xff0c;周内累计跌幅近4%。业内人士以为&#xff0c;美国农业部超预期调高20…

pinyin-pro库使用方式

pinyin-pro 是一个专业的 JavaScript 中文转拼音的库&#xff0c;具备多音字识别准确、体积轻量、性能优异、功能丰富等特点。 pinyin-pro官网链接&#xff1a;介绍 | pinyin-pro 运行展示 pinyin-pro安装命令&#xff1a; # 选择一个你使用的包管理器进行安装即可# NPM $ n…

学网络必懂的华为CSS堆叠技术

知识改变命运&#xff0c;技术就是要分享&#xff0c;有问题随时联系&#xff0c;免费答疑&#xff0c;欢迎联系&#xff01; 厦门微思网络​​​​​​https://www.xmws.cn 华为认证\华为HCIA-Datacom\华为HCIP-Datacom\华为HCIE-Datacom Linux\RHCE\RHCE 9.0\RHCA\ Oracle OC…

2024多系统萎缩最新全球特效药治疗进展

多系统萎缩是一种罕见的神经退行性疾病&#xff0c;由于缺乏有效的治疗方法&#xff0c;患者经常面临症状无法缓解和生活品质下降的困扰。然而&#xff0c;近期刘家峰大夫基于中医理论研究和临床实践&#xff0c;采用中药治疗多系统萎缩取得了显著疗效&#xff0c;给患者带来了…

mysql原理--undo日志2

1.概述 上一章我们主要唠叨了为什么需要 undo日志 &#xff0c;以及 INSERT 、 DELETE 、 UPDATE 这些会对数据做改动的语句都会产生什么类型的 undo日志 &#xff0c;还有不同类型的 undo日志 的具体格式是什么。本章会继续唠叨这些 undo日志 会被具体写到什么地方&#xff0c…

RMI简介

RMI 介绍 RMI (Remote Method Invocation) 模型是一种分布式对象应用&#xff0c;使用 RMI 技术可以使一个 JVM 中的对象&#xff0c;调用另一个 JVM 中的对象方法并获取调用结果。这里的另一个 JVM 可以在同一台计算机也可以是远程计算机。因此&#xff0c;RMI 意味着需要一个…

Spring MVC中的一些常用注解

目录 RequestMapping 实现路由映射 限制请求方式 PathVariable 从url中获取变量的值 更改绑定参数的名字 RequestParam 可以传递集合 更改绑定参数的名字 可修改是否为必传参数 RequestBody 获取请求正文的内容 可修改是否为必传参数 RequestPart 可以支持上传…

Android中的SPI实现

Android中的SPI实现 SPI是JVM世界中的标准API&#xff0c;但在Android应用程序中并不常用。然而&#xff0c;它可以非常有用地实现插件架构。让我们探讨一下如何在Android中利用SPI。 问题 在Android中&#xff0c;不同的提供者为推送功能提供服务&#xff0c;而在大型项目中…