JavaEE07 定时器的使用和模拟实现

news2025/1/24 1:29:06

前言

前面我们聊了很多关于阻塞队列,单例模式等的应用,今天我们就来聊聊定时器的功能和模拟实现,其实定时器的实现在我们的日常生活中也很常见,比如说平常创建一些定时任务,定时开关机,定时去发表一篇qq空间等等,今天我们就来简单实现一个定时器.

1.JVM提供的定时器的使用

在自己实现之前,让我们先去看看JVM给我们提供好的定时器是如何使用的吧

以下是一个简单的实例

我们首先创建了一个定时器对象,定时器对象里面有一个schedule方法,这个方法最常用的是有两个参数组成的,一个是task任务,另一个是delay:相对现在的时间延迟多久发生这个事件,单位是毫秒,我们发现定时任务做完这个进程并没有结束,这是因为你没有让他结束,只能手动结束

timer也提供了一个cancel方法来执行这个操作.

其实这是因为定时器内部存在一个扫描线程会扫描任务队列,这个线程属于前台线程,会阻止进程的结束.

下面附上执行代码

public static void main(String[] args) {
        Timer timer = new Timer();
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                System.out.println("确实挺不错的");
            }
        };
        timer.schedule(task,2000);
        TimerTask task1 = new TimerTask() {
            @Override
            public void run() {
                System.out.println("你们觉得怎么样");
            }
        };
        timer.schedule(task1,1000);

    }

2.定时器的模拟实现

首先MyTimer中应该有一个阻塞队列,一个扫描线程和一个schedule方法

private Thread t = null;
private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
public void schedule(Runnable runnable,long delay){
       synchronized (lock){
           MyTimerTask task = new MyTimerTask(runnable,delay);
           queue.offer(task);
           lock.notify();
       }
    }

然后我们再想一下task的实现,首先应该有一个执行时间,由于要放入优先级队列里来实现定时器的功能,所以我们的task也需要实现compareable接口或compartor接口,我们这里使用compareable接口来实现.

然后我们是要使用扫描线程来操作和执行这个任务,所以我们应该实现Runnable接口或者持有这个接口.此时你可以理解runnable的实现就是我的任务需要做什么

我们还要提供一个run方法给下面的扫描线程来执行

class MyTimerTask implements Comparable<MyTimerTask>{
    //什么时间运行这个任务  -- ms级别的时间戳
    private long time;
    private Runnable runnable;

    //delay期望是相对时间
    public MyTimerTask(Runnable runnable,long delay){
        this.runnable = runnable;
        this.time = System.currentTimeMillis() + delay;
    }
    public void run(){
        runnable.run();
    }

    //实现比较,这个地方记不得o1-o2 就去试一试
    @Override
    public int compareTo(MyTimerTask o) {
        return (int)(this.time - o.time);
  }
  public long getTime(){
        return time;
  }

}

下面我们继续实现我们的定时器,我们在构造方法内部实现扫描线程对阻塞队列的拿取任务执行任务的操作,取到任务就执行,遇见空队列我们就进行阻塞,又加入任务我们就进行唤醒,于是就有了一把锁来完成对阻塞队列的执行和添加任务的操作

这里也是先获取一下距离当前时间最短的任务,看是否到时间,到达就立即取出并执行,没到就阻塞(最多阻塞时间为预计时间减去当前时间,这是因为在阻塞的期间也可能有更新的任务添加进来)

public  MyTimer(){
        //扫描线程
        t = new Thread(()->{
            while(true){
                //引入锁目的是保护队列,所以得把队列的操作保护起来
               synchronized (lock){
                   while(queue.isEmpty()){
                       try {
                           lock.wait();
                       } catch (InterruptedException e) {
                           throw new RuntimeException(e);
                       }
                   }
                   MyTimerTask task =  queue.peek();
                   //获取当前时间
                   long curTime = System.currentTimeMillis();
                   if(curTime >= task.getTime()){
                       queue.poll();
                       task.run();
                   }else{
                       //时间还没到也需要等待,一直执行while循环判断,称之为忙等
                       //这一般是一个不太好的做法
                       //使用sleep也不太合适
                       //万一在sleep过程中有线程调用了schedule
                       //而且sleep也不能解锁
                       try {
                           //新任务来被唤醒一次
                           //根据新的任务重新计算等待时间
                           //或者是wait过程中没有新的任务,但是时间到了
                           lock.wait(task.getTime() - curTime);
                       } catch (InterruptedException e) {
                           throw new RuntimeException(e);
                       }
                   }
               }

            }
        });
        t.start();

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

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

相关文章

Java LeetCode篇-深入了解二叉树的经典解法(多种方式实现:构造二叉树)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 从前序与中序遍历序列来构造二叉树 1.1 实现从前序与中序遍历序列来构造二叉树思路 1.2 代码实现从前序与中序遍历序列来构造二叉树 2.0 从中序与后序遍历序…

Unicode编码解码

一、Unicode概述 Unicode是一种字符编码标准&#xff0c;旨在解决不同字符集之间的兼容性问题。它为全球所有语言提供了一种统一的编码方式&#xff0c;使得各种字符能够在计算机系统中正确显示和处理。Unicode字符集包含了世界上几乎所有的字符&#xff0c;包括中文字符、英文…

Java第十二章(合集类)

概述 集合可以看作一个容器&#xff1b;集合中的每个对象&#xff0c;可以很容易取出存放&#xff0c;还可以按照一定的顺序摆放 提到容器&#xff0c;不难想到数组&#xff0c;集合类和数组的不同之处是&#xff0c;数组的长度是固定的&#xff0c;集合的长度是可变 的&…

操作系统考研考点补充(王道408)

文章目录 前言计算机系统概述OS的基本概念OS的发展历程OS的运行机制OS体系结构OS引导虚拟机 前言 学校OS课程的知识和408有一定的重叠&#xff0c;但是还不太够&#xff0c;因此我又一次打开了王道的OS课程。 这个笔记同理&#xff0c;只记最关键的内容和思考&#xff0c;直接…

LeetCode Hot100 78.子集

题目&#xff1a; 给你一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返回该数组所有可能的子集&#xff08;幂集&#xff09;。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 方法&#xff1a;灵神 选 or 不选 class Solution {private final List&…

报错:Parsed mapper file: ‘file mapper.xml 导致无法启动

报错 &#xff1a; Logging initialized using class org.apache.ibatis.logging.stdout.StdOutImpl adapter. Registered plugin: com.github.yulichang.interceptor.MPJInterceptor3b2c8bda Parsed mapper file: file [/Mapper.xml] application无法启动 我这边产生原因是项…

【Unity】Addressable包资源加载失败:CRC Mismatch.

Error while downloading Asset Bundle: CRC Mismatch. 是资源下载校验失败&#xff0c;但是资源和上次打包的资源是一样的。没有排查到原因&#xff0c;在谷歌搜索后看到 大概就是指Unity版本修改后打包&#xff0c;会破坏原来的CRC信息&#xff0c;导致导报出来的资源无法通…

mybatis数据输出-单个简单类型和返回实体类型对象以及别名设置

1、建库建表 CREATE DATABASE mybatis-example;USE mybatis-example;CREATE TABLE t_emp(emp_id INT AUTO_INCREMENT,emp_name CHAR(100),emp_salary DOUBLE(10,5),PRIMARY KEY(emp_id) );INSERT INTO t_emp(emp_name,emp_salary) VALUES("tom",200.33); INSERT INTO…

APP备案,最新获取安卓签名文件中MD5等信息方法

1.通过签名文件获取SHA1和SHA256 直接通过cmd执行命令 keytool -list -v -keystore xxxxx/xxx/xx/xxx.keystore输入后回车会提示输入密码库口令&#xff0c;直接输入Keystore密码&#xff08;输入过程中终端上不会显示&#xff0c;输完回车就行&#xff09; 2.获取md5 由于…

Android Studio Hedgehog | 2023.1.1(刺猬)

Android Gradle 插件和 Android Studio 兼容性 Android Studio 构建系统基于 Gradle&#xff0c;并且 Android Gradle 插件 (AGP) 添加了一些特定于构建 Android 应用程序的功能。下表列出了每个版本的 Android Studio 所需的 AGP 版本。 Android Studio versionRequired AG…

代码随想录二刷 |二叉树 |填充每个节点的下一个右侧节点指针II

代码随想录二刷 &#xff5c;二叉树 &#xff5c;填充每个节点的下一个右侧节点指针II 题目描述解题思路代码实现 题目描述 117.填充每个节点的下一个右侧节点指针II 给定一个二叉树&#xff1a; struct Node {int val;Node *left;Node *right;Node *next; }填充它的每个 ne…

上网行为管理软件如何监控员工访问网址信息

上网行为管理软件可以通过域之盾软件来监控员工访问的网址信息&#xff1a; 主要方式↓ 1、网页日志记录 上网行为管理软件可以通过网页日志记录功能&#xff0c;完整地记录员工在工作中访问的所有网站&#xff0c;包括访问时间、访问页面、访问方式等&#xff0c;从而实现对…

【Pytorch】Fizz Buzz

文章目录 1 数据编码2 网络搭建3 网络配置&#xff0c;训练4 结果预测5 翻车现场 学习参考来自&#xff1a; Fizz Buzz in Tensorflowhttps://github.com/wmn7/ML_Practice/tree/master/2019_06_10Fizz Buzz in Pytorch I need you to print the numbers from 1 to 100, excep…

快捷切换raw页面到repo页面-Raw2Repo插件

Raw2Repo By Rick &#x1f4d6;快捷切换代码托管平台raw页面到repo页面 &#x1f517;github链接 https://github.com/rickhqh/Raw2Repo ✨Features 功能&#xff1a; ✅单击 Raw2Repo 插件按钮&#xff0c;即可跳转到相应的代码仓库页面。✅支持 GitHub、Gitee、GitCode …

ChatGPT OpenAI API请求限制 尝试解决

1. OpenAI API请求限制 Retrying langchain.chat_models.openai.ChatOpenAI.completion_with_retry.._completion_with_retry in 4.0 seconds as it raised RateLimitError: Rate limit reached for gpt-3.5-turbo-16k in organization org-U7I2eKpAo6xA7RUa2Nq307ae on reques…

Ignis - Interactive Fire System

Ignis - 点火、蔓延、熄灭、定制! 全方位火焰系统。 这个插件在21年的项目中使用过很好用值使用概述 想玩火吗?如果想的话,那么Ignis就是你的最佳工具。有了Ignis,你可以把任何物体、植被或带皮带骨的网状物转换为可燃物体,它就会自动着火。然后,火焰可以蔓延,点燃其他物…

C++_函数重载

前言&#xff1a; 函数重载的意思就是可以有多个同名函数存在&#xff0c;但是这些同名函数的参数列表有着不同情形&#xff0c;以便区分。在C中&#xff0c;支持在同一作用域下可以声明、定义多个同名函数&#xff0c;但是这些函数的形参类型&#xff0c;类型顺序以及参数个数…

dcat admin多后台和自定义登录

多后台按照教程配置 https://learnku.com/docs/dcat-admin/2.x/multi-application-multi-background/8475 自定义登录 我的新后台的登录需要另外一个用户表&#xff0c;所以原来的逻辑要修改一下。 1、首先是模板修改 参考连接 https://learnku.com/docs/dcat-admin/2.x/ba…

UML案例分析

首先需要花大约20分钟来思考解决这个问题&#xff0c;如果对问题不是很熟悉&#xff0c;也可以在完成题目之后&#xff0c;找相关的资料翻阅&#xff08;例如看UML类图的基本情况&#xff0c;UML状态图的基本情况&#xff0c;然后结合这些信息 做一个自我评价&#xff0c;看这个…