定时器--JAVA

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

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();
    }
}

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

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

相关文章

docker-ce 安装与国内源配置 | Ubuntu 20.04

博客原文 文章目录 让apt可以支持HTTPS将官方Docker库的GPG公钥添加到系统中将Docker库添加到APT里更新包列表为了确保修改生效&#xff0c;让新的安装从Docker库里获取&#xff0c;而不是从Ubuntu自己的库里获取&#xff0c;执行&#xff1a;安装 docker-ce配置 docker 阿里源…

《SPSS统计学基础与实证研究应用精解》视频讲解:SPSS 与 Stata、Python 的对比

《SPSS统计学基础与实证研究应用精解》1.5 视频讲解 视频为《SPSS统计学基础与实证研究应用精解》张甜 杨维忠著 清华大学出版社 一书的随书赠送视频讲解1.5节内容。本书已正式出版上市&#xff0c;当当、京东、淘宝等平台热销中&#xff0c;搜索书名即可。本书旨在手把手教会使…

【html+css+js】实例自习笔记–前端基础知识–溢出的文字省略号显示

【htmlcssjs】实例自习笔记–前端基础知识–溢出的文字省略号显示 【CSS面试题】溢出的文字省略号显示 问题的描述 单行文本溢出显示省略号 多行文本溢出显示省略号 为了达到上面这种省略号的效果&#xff0c;我们举一个栗子 1.单行文本溢出显示省略号–必须满足三个条件 先…

【搭建个人知识库-3】

搭建个人知识库-3 1 大模型开发范式1.1 RAG原理1.2 LangChain框架1.3 构建向量数据库1.4 构建知识库助手1.5 Web Demo部署 2 动手实践2.1 环境配置2.2 知识库搭建2.2.1 数据收集2.2.2 加载数据2.2.3 构建向量数据库 2.3 InternLM接入LangChain2.4 构建检索问答链1 加载向量数据…

Maven环境搭建及Maven部分目录分析

一、安装Maven Maven 本身就是⼀套由 Java 开发的软件&#xff0c;所以 Maven 的运⾏需要依赖 JDK 环境。在安装 Maven 之前请 确认JDK 是否配置正确&#xff08;主要依赖 JAVA_HOME 环境变量&#xff09;。如果没有正确安装和配置 JDK &#xff0c;则运⾏ Maven 时 会出现以下…

实战 | 某电商平台类目SKU数获取与可视化展示

一、项目背景 最近又及年底&#xff0c;各类分析与规划报告纷至沓来&#xff0c;于是接到了公司平台类目商品增长方向的分析需求&#xff0c;其中需要结合外部电商平台做对比。我选择了国内某电商平台作为比较对象&#xff0c;通过获取最细层级前台类目下的SKU数以及结构占比&…

大数据深度学习ResNet深度残差网络详解:网络结构解读与PyTorch实现教程

文章目录 大数据深度学习ResNet深度残差网络详解&#xff1a;网络结构解读与PyTorch实现教程一、深度残差网络&#xff08;Deep Residual Networks&#xff09;简介深度学习与网络深度的挑战残差学习的提出为什么ResNet有效&#xff1f; 二、深度学习与梯度消失问题梯度消失问题…

D20XB60-ASEMI开关电源桥堆D20XB60

编辑&#xff1a;ll D20XB60-ASEMI开关电源桥堆D20XB60 型号&#xff1a;D20XB60 品牌&#xff1a;ASEMI 封装&#xff1a;GBJ-5&#xff08;带康铜丝&#xff09; 平均正向整流电流&#xff08;Id&#xff09;&#xff1a;20A 最大反向击穿电压&#xff08;VRM&#xff…

机器学习学习笔记(吴恩达)(第三课第一周)(无监督算法,K-means、异常检测)

欢迎 聚类算法&#xff1a; 无监督学习&#xff1a;聚类、异常检测 推荐算法&#xff1a; 强化学习&#xff1a; 聚类&#xff08;Clustering&#xff09; 聚类算法&#xff1a;查看大量数据点并自动找到彼此相关或相似的数据点。是一种无监督学习算法 聚类与二院监督…

常见的限流算法

本文已收录至我的个人网站&#xff1a;程序员波特&#xff0c;主要记录Java相关技术系列教程&#xff0c;共享电子书、Java学习路线、视频教程、简历模板和面试题等学习资源&#xff0c;让想要学习的你&#xff0c;不再迷茫。 天下武学出同源 正所谓天下武学殊途同归&#xff…

用二维码介绍产品详情,扫码查看图文并茂的宣传册

传统的产品宣传方式&#xff0c;往往以产品手册、宣传单等纸质物料为主&#xff0c;更新成本高昂&#xff0c;一旦修改内容&#xff0c;就必须重新印刷&#xff0c;而且不易携带和保存&#xff0c;影响宣传效果和客户体验。 为了避免上述问题&#xff0c;可以在草料上搭建产品…

Python之循环判断语句

一、if判断语句 1. if...else if 条件: 满足条件时要做的事情1 满足条件时要做的事情2 ...... else: 不满足条件时要做的事情1 不满足条件时要做的事情2 ...... # -*- coding:utf-8 -*- age input("请输入年龄:") age int(age) if age > 18:print("已经成…

云贝教育 |【技术文章】存储对象的LIBRARY CACHE LOCK/PIN实验(一)

注: 本文为云贝教育 刘峰 原创&#xff0c;请尊重知识产权&#xff0c;转发请注明出处&#xff0c;不接受任何抄袭、演绎和未经注明出处的转载。 实验环境 操作系统&#xff1a;Red Hat Enterprise Linux release 8.8 (Ootpa) 数据库&#xff1a;oracle Version 19.3.0.0.0 …

Openstack云计算(六)Openstack环境对接ceph

一、实施步骤&#xff1a; &#xff08;1&#xff09;客户端也要有cent用户&#xff1a; useradd cent && echo "123" | passwd --stdin cent echo -e Defaults:cent !requiretty\ncent ALL (root) NOPASSWD:ALL | tee /etc/sudoers.d/ceph chmod 440 /et…

Endothelin-1(内皮素-1) ELISA kit

灵敏、快速的内皮素-1 ELISA试剂盒&#xff0c;适用于心血管和应激相关研究 内皮素&#xff08;Endothelin, ET&#xff09;是由血管内皮细胞产生的异肽&#xff0c;具有强大的血管收缩活性。这种肽由三个独立的基因编码&#xff0c;经过加工产生39个残基的 大ET 分子&#xff…

Linux的SSH远程管理和服务器之间的免密连接

目录 一、远程管理基础 1.ssh协议 2.ssh原理 3、使用ssh协议传输的命令 4.登录方法 二、免密连接 1.免密连接的原理 2.实战 一、远程管理基础 1.ssh协议 ssh协议是基于C/S机构的安全通道协议&#xff0c;通信数据进行加密处理&#xff0c;用于远程管理。 ssh的服务名…

Linux多网卡绑定实现负载均衡详解

将多块网卡绑定同一IP地址对外提供服务&#xff0c;可以实现高可用或者负载均衡。直接给两块网卡设置同一IP地址是不可以的。通过 bonding&#xff0c;虚拟一块网卡对外提供连接&#xff0c;物理网卡的被修改为相同的MAC地址。 目录 1、bond的作用 2、Bonding聚合链路工作模…

低代码开发,企业的金钥匙,工业4.0转型的催化剂

近年&#xff0c;国内工业产值开始逐渐放缓&#xff0c;人口红利也开始逐渐消退&#xff0c;工业领域现在面临着高能耗、高投入、高风险以及低效益的困境。我国将“先进制造”作为十四五规划重要目标&#xff0c;推动工业领域实体经济、智能化转型、实现数字化、加快工业互联网…

PattPatel-“Introduction to Computing Systems“(3)期末样卷题目解析:C语言与汇编语言转化

上接&#xff08;1&#xff09;basic ideas和与解析&#xff08;1&#xff09; 核心思路还是借具体题目来理解书中的两条basic ideas——abstraction of layers与think both softwarely and hardwarely。 C语言与汇编语言的转化 题目的要求是将下面的这段代码用LC-3改写。 这…

SQL备忘--集合运算

前言 本文讨论的是两个子查询结果的合并问题&#xff0c; 是行维度下的合并处理 例如子查询A查出5条记录、子查询B查出3条记录&#xff0c;那么将两个结果合并&#xff0c;则共返回8条记录 行维度上要能进行合并&#xff0c;前置要求是&#xff1a;子查询的列字段是相同的&…