定时器和Timer

news2025/6/9 12:24:01

♫什么是定时器

定时器是一种计时设备,通过定时器可以让某段代码达到设定的时间后再执行。定时器是一个常用的组件,如在进行网络编程时就常用定时器来定时重发数据包、定时检测网络连接状态、定时更新缓存等操作。

♫实现一个定时器

定时器需要有一个优先级的阻塞队列(定时器每次都先执行时间小的,按照时间小的作为优先级)来存储待执行的任务,还需要一有个扫描线程一直扫描队首元素,来判定到没到和执行任务的时间。

注:此处的优先级队列会在多线程环境下使用,需要考虑线程安全问题,故可以使用标准库提供的线程安全的阻塞队列:PriorityBlockingQueue

使用 MyTimer 类用来描述定时器,MyTimer 类的属性包括一个优先级队列和一个扫描线程:

public class MyTimer {
    //扫描线程
    private Thread t = null;
    //带有优先级的阻塞队列,用来存储任务
    private PriorityBlockingQueue<MyTask> queue = null;
}
还需要有一个 MyTask 类用于描述一个任务 ( 作为  MyTimer 的内部类 ), 里面包含一个  Runnable 对象(描述任务执行的内容)和一个 time( 毫秒时间戳来描述任务执行的时刻),由于这个对象需要放到优先队列中, 因此需要实现 Comparable 接口或使用 Comparator 接口单独写个比较器:
    class MyTask implements Comparable<MyTask> {
        //要执行的任务内容
        private Runnable runnable;
        //执行的时刻
        private long time;

        public MyTask(Runnable runnable, long time) {
            this.runnable = runnable;
            this.time = time;
        }
        //获取当前任务的时间
        public long getTime() {
            return time;
        }
        //执行任务
        public void run() {
            runnable.run();
        }

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

MyTimer 类提供一个 schedule 方法,用于注册一个任务,并指定这个任务多长时间后执行:

  //添加任务
  public void schedule(Runnable runnable, long after) {
        MyTask myTask = new MyTask(runnable, System.currentTimeMillis() + after);
        queue.put(myTask);
    }

在 MyTimer 的构造方法里不断判断任务是否开始执行:

//在构造方法中扫描线程
    public MyTimer() {
        t = new Thread(() -> {
            while (true) {
                try {
                    MyTask myTask = queue.take();
                    long time = System.currentTimeMillis();
                    if (time < myTask.getTime()) {
                        queue.put(myTask);
                    } else {
                        myTask.run();
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }

但是当前这个代码中存在一个严重的问题,就是 while (true) 转的太快了,造成了无意义的 CPU 浪费,我们可以使用 wait 和 notify 使任务进入阻塞等待,指定一定 wait 时间,时间到或有新任务(可能执行时间比当前任务靠前),则通过 notify 唤醒:

    //添加任务
    public void schedule(Runnable runnable, long after) {
        MyTask myTask = new MyTask(runnable, System.currentTimeMillis() + after);
        queue.put(myTask);
        synchronized (this) {
            this.notify();
        }
    }

    //在构造方法中扫描线程
    public MyTimer() {
        t = new Thread(() -> {
            while (true) {
                try {
                    MyTask myTask = queue.take();
                    long time = System.currentTimeMillis();
                    if (time < myTask.getTime()) {
                        queue.put(myTask);
                        synchronized (this) {
                            this.wait(myTask.getTime() - time);
                        }
                    } else {
                        myTask.run();
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }

此时虽然解决了“忙等”的问题,但还有一个线程安全的问题:如果线程1运行到 this.wait(myTask.getTime() - time) 之前刚好被调度去其他线程执行添加新的任务操作,之后再调度到线程1执行wait操作,即先执行了 schedule() 里的 notify 后再执行构造方法里的 wait ,此时如果新添加的任务的执行时间更靠前仍不能提前唤醒线程1,就会有问题。上述问题是由于取堆顶元素和wait操作不是原子性的,故需要通过synchronized保证原子性:

    //在构造方法中扫描线程
    public MyTimer() {
        t = new Thread(() -> {
            while (true) {
                try {
                    synchronized (this) {
                        MyTask myTask = queue.take();
                        long time = System.currentTimeMillis();
                        if (time < myTask.getTime()) {
                            queue.put(myTask);
                            this.wait(myTask.getTime() - time);
                        } else {
                            myTask.run();
                        }
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }

♫Timer

Timer是标准库中的定时器。 Timer 类的核心方法为 schedule,schedule 包含两个参数:   第一个参数指定即将要执行的任务代码, 第二个参数指定多长时间之后执行 ( 单位为毫秒 ):
public class Test {
    public static void main(String[] args) {
        Timer timer = new Timer();
        //1000ms后打印A
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("A");
            }
        }, 1000);
        //2000ms后打印B
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("B");
            }
        }, 2000);
        //3000ms后打印C
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("C");
            }
        }, 3000);
    }
}

运行结果:

注:TimerTask实现了Runnable接口,TimerTask就是对Runnable的进一步封装

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

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

相关文章

chat2db初步使用和体验AI

今天下载chat2db体验了下将主要功能和使用截图总结下&#xff1a; 功能&#xff1a; 1.传统客户端能使用的功能基本都有&#xff0c;并且增加了导出excel等便捷的能力。 2.报表功能&#xff1a; 可以根据查询结果进行对应报表可视化显示 3.AI解析sql 可以根据输入的汉语例如…

比如我的企业也想要申报抖音白名单进行直播招聘,该如何操作呢?

比如现在我的企业也想要申报抖音白名单进行直播招聘&#xff0c;该如何申报呢&#xff1f; 答&#xff1a;相关企业/商家在提供自身相关的招聘资质后&#xff0c;就可以进行抖音直播报白名单的申请&#xff0c;在审核通过之后就可以百分百的在抖音直播时畅聊关于自家企业的招聘…

全志XR806基于FreeRTOS的SoftAp配网实现

1.环境搭建 由于电脑上之前就有开发其他设备用的ubuntu18.06虚拟机环境&#xff0c;就在此环境基础上进行开发。基本环境搭建参考官方文档进行&#xff1a; 全志XR806开发板开发环境搭建 2.功能实现 2.1设计思路 从官方下载的SDK开发包project/example目录下有基本功能实现…

Java后端开发——JDBC组件

JDBC&#xff08;Java Database Connectivity&#xff09;是Java SE平台的一种标准API&#xff0c;它提供了一种标准的方法来访问关系型数据库&#xff0c;使得Java程序能够与各种不同的数据库进行交互&#xff0c;这篇文章我们来进行实验体验一下。 自定义JDBC连接工具类 1.编…

【快刊推荐】EI快刊盘点,仅29天录用,国人友好,接收领域广!

综合类EI • 快刊推荐 01 期刊简介 检索数据库&#xff1a;EI &Scopus &Google Scholar 版面类别&#xff1a;正刊 数据库收录年份&#xff1a;2009年 国人占比&#xff1a;47%&#xff0c;对国人友好 年发文量&#xff1a;20篇左右 02 接收领域 生物&#xff0…

使用C++的QT框架实现俄罗斯方块

今天实现一个简单的俄罗斯方块&#xff0c;网上别人写的都比较长还复杂&#xff0c;我就写了一个简单的&#xff0c;可以实现功能的俄罗斯方块&#xff0c;使用的是C语言&#xff0c;框架都可以&#xff0c;主要是逻辑思路有都可以实现 我这边实现的逻辑为两个数组包含各个动态…

“没有酒瓶”的新春礼酒,泸州老窖的颠覆性之作

执笔 | 萧 萧 编辑 | 扬 灵 没有想到&#xff0c;新春礼酒还能跳出生肖酒造型桎梏&#xff0c;开创出“没有酒瓶的白酒”。 没有想到&#xff0c;即将要发布的新品就“藏”在每一位参会者都触手可及的餐桌正中。 没有想到&#xff0c;首发定价如此“实诚”&#xff0c;加…

python爬取Web of science论文信息

一、python爬取WOS总体思路 (一)拟实现功能描述 wos里面&#xff0c;爬取论文的名称&#xff0c;作者名称&#xff0c;作者单位&#xff0c;引用数量 要求&#xff1a;英文论文、期刊无论好坏 检索关键词&#xff1a;zhejiang academy of agricultural sciences、 xianghu lab…

【并查集】93 岛屿数量

岛屿数量 题解1 DFS&#xff08;图论经典方法&#xff09;题解2 BFS(遍历&#xff08;DFS展开【顺序不同】&#xff09;)题解3 并查集&#xff08;学习理解&#xff09; 给你一个由 ‘1’&#xff08;陆地&#xff09;和 ‘0’&#xff08;水&#xff09;组成的的二维网格&…

TikTok未来展望:数字创新的下一浪潮

随着全球数字时代的蓬勃发展&#xff0c;TikTok已经成为一个巨大的现象&#xff0c;吸引了数以亿计的用户&#xff0c;尤其是年轻一代。这个短视频分享平台已经深刻影响了社交媒体、娱乐和文化。然而&#xff0c;TikTok未来的发展前景如何&#xff1f;本文将探讨TikTok的未来展…

哈希的开放定址法的实现【C++】

哈希的开放定址法的实现【C】 1 概述2 线性探测2.1 插入2.2 查找2.3 删除2.6 完整代码2.5 线性探测的优缺点 3. 二次探测 1 概述 开放定址法也叫闭散列&#xff0c;是解决哈希冲突的一种方法&#xff0c;当发生哈希冲突之后&#xff0c;如果哈希表没有被装满(正常情况哈希表不会…

饥荒联机版 Don‘t Starve Together(WinMac)最新中文学习版

《饥荒联机版》是由Klei自主开发的开放世界冒险游戏。在这个游戏中&#xff0c;玩家将扮演各种各样的人物&#xff0c;这些人物不幸来到了一个神秘的异世界。在旅行中&#xff0c;玩家会邂逅性格各异、能力独特的同伴们&#xff0c;并和他们一起生存下去并征服异世界。游戏中的…

每日一题 117. 填充每个节点的下一个右侧节点指针 II (中等,树)

BFS&#xff0c;一层层去搜索整棵树&#xff0c;然后建立next关系即可&#xff0c;下面给出的代码的空间复杂度是O(n)的O(1) 的做法&#xff0c;当构建完上一层的next关系后&#xff0c;我们就可以像链表一样从左到右访问上一层的节点&#xff0c;显然在访问的过程中&#xff0…

N-132基于springboot,vue人事管理系统

开发工具&#xff1a;IDEA 服务器&#xff1a;Tomcat9.0&#xff0c; jdk1.8 项目构建&#xff1a;maven 数据库&#xff1a;mysql5.7 系统分前后台&#xff0c;项目采用前后端分离 前端技术&#xff1a;vueelementUI 服务端技术&#xff1a;springbootmybatis-plus 本项…

自定义类型结构体(下)

目录 结构体传参结构体实现位段什么是位段位段的内存分配位段的跨平台问题总结&#xff1a; 位段的应用位段使用的注意事项** 感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接 &#x1f412;&#x1f412;&#x1f412; 个人主页 &#x1f978;&#x1f978;&a…

DataCastle企业风险算法赛实战(进阶难度)

目录 一、数据读取及分析 1、数据读取 2、数据分析 二、数据挖掘 三、模型构建及评估 四、划重点 推荐相关文章 去年在DataCastle上参加了华录杯算法赛&#xff0c;初赛前10、进复赛就没打了。相比于之前文章 kaggle风控建模实战&#xff08;文末附链接&#xff09;&…

智慧财务的未来

信息化时代&#xff0c;财务管理不再局限于传统的手工操作&#xff0c;而是借助RPA技术实现了自动化、智能化的转型。智慧财务作为财务管理的一种新模式&#xff0c;将为企业提供更加高效、便捷的服务&#xff0c;使企业能够更好地适应市场需求的变化&#xff0c;在瞬息万变的市…

批量删除文件名中的某些文字

怎么批量删除文件名中的某些文字&#xff1f;在整理电脑文件的时候&#xff0c;我们经常需要处理大量文件的重命名工作。当你的文件名称包含不必要或重复的字符时&#xff0c;可以进行批量删除&#xff0c;以使文件名称更简洁、清晰&#xff0c;提高可读性和识别性。例如&#…

【蓝桥杯软件赛 零基础备赛20周】第2周——常考知识点+判题

文章目录 0. 第1周答疑1. 常考知识点2. 蓝桥杯怎么判题2.1 判题系统如何判题2.2 测试数据和得分的关系2.3 自己做测试数据 3. 备赛计划4. 本周刷题 0. 第1周答疑 问题1&#xff1a;蓝桥杯怎么报名&#xff0c;什么时候报名&#xff1f; 答&#xff1a;集体报名或个人报名。大…

【word技巧】ABCD选项如何对齐?

使用word文件制作试卷&#xff0c;如何将ABCD选项全部设置对齐&#xff1f;除了一直按空格或者Tab键以外&#xff0c;还有其他方法吗&#xff1f;今天分享如何将ABCD选项对齐。 首先&#xff0c;我们打开【替换和查找】&#xff0c;在查找内容输入空格&#xff0c;然后点击全部…