JavaEE初阶——多线程(七)——定时器

在这里插入图片描述

T04BF

👋专栏: 算法|JAVA|MySQL|C语言

🫵 小比特 大梦想

此篇文章与大家分享多线程的第七篇文章——关于定时器
如果有不足的或者错误的请您指出!

目录

    • 4.定时器
      • 4.1标准库提供的定时器
      • 4.2自己实现一个定时器
        • 4.2.1任务类
        • 4.2.2Timer类
        • 4.2.3 有一个线程来负责执行这里的任务

4.定时器

所谓定时器就是类似于闹钟效果,指定一个任务给他,这个任务不会立即执行.而是到达指定的时间后才执行
定时器在实际开发中非常重要,甚至会单独封装成一个服务器,给整个分布式系统使用

4.1标准库提供的定时器

在这里插入图片描述
这里的TimeTask实际上是继承了Runnable接口的
在这里插入图片描述
同时,Timer内部包含的也是前台线程,阻止了进程结束

4.2自己实现一个定时器

需求:能够延迟执行任务 ,能够管理多个任务

需要有:定义一个类.表示一个任务

通过一定的数据结构来保存多个任务

还需要有一个线程,来负责执行这里的任务(在指定之间内去执行)

4.2.1任务类
public class MyTimeTask {
   /*
   当前自己定义的任务里面要保存执行任务的绝对的时间(时间戳)
   为了后续线程执行的时候,可以方便的判定,该任务是否执行
   */
    private long time;
    
    private Runnable runnable;
    public MyTimeTask(Runnable runnable ,long delay) {
        this.time = System.currentTimeMillis() + delay;//手动换算时间
        this.runnable = runnable;
    }
}
4.2.2Timer类
public class MyTimer {
    /*
这里实际上最直观的是使用List
但是如果这里元素很多,就需要不停循环的扫描这里的任务,分别判定是否要执行
那么我们就可以将这些任务通过优先级队列保存起来,按照时间作为优先级队列的先后标准,就可以做到,队首元素就是最先要执行的任务,那么线程扫描的时候直接判断队首元素即可
那么我们就要对任务类实现comparable接口
     */
    PriorityQueue<MyTimeTask> queue = new PriorityQueue<>();
    
    
    public void schedule(Runnable runnable,int delay) {
        MyTimeTask timeTask = new MyTimeTask(runnable,delay);
        queue.offer(timeTask);
    }
}

此时别忘了对MyTimeTask类实现Comparable接口

public class MyTimeTask implements Comparable<MyTimeTask>{
   /*
   当前自己定义的任务里面要保存执行任务的绝对的时间(时间戳)
   为了后续线程执行的时候,可以方便的判定,该任务是否执行
   */
    private long time;

    private Runnable runnable;
    public MyTimeTask(Runnable runnable ,long delay) {
        this.time = System.currentTimeMillis() + delay;//手动换算时间
        this.runnable = runnable;
    }


    @Override
    public int compareTo(MyTimeTask o) {
        return (int)(this.time - o.time);
    }
}
4.2.3 有一个线程来负责执行这里的任务
public class MyTimer {
    /*
这里实际上最直观的是使用List
但是如果这里元素很多,就需要不停循环的扫描这里的任务,分别判定是否要执行
那么我们就可以将这些任务通过优先级队列保存起来,按照时间作为优先级队列的先后标准,就可以做到,队首元素就是最先要执行的任务,那么线程扫描的时候直接判断队首元素即可
那么我们就要对任务类实现comparable接口
     */
    PriorityQueue<MyTimeTask> queue = new PriorityQueue<>();

    public MyTimer () {
        Thread t = new Thread (() -> {
           while(true) {
               //此时就要判断当前优先级任务队列的头任务是否到达执行时间了
               //如果到达则执行,不到达则循环判断
           }
        });
    }
    public void schedule(Runnable runnable,int delay) {
        MyTimeTask timeTask = new MyTimeTask(runnable,delay);
        queue.offer(timeTask);
    }


}
public class MyTimeTask implements Comparable<MyTimeTask>{
   /*
   当前自己定义的任务里面要保存执行任务的绝对的时间(时间戳)
   为了后续线程执行的时候,可以方便的判定,该任务是否执行
   */
    private long time;

    private Runnable runnable;
    public MyTimeTask(Runnable runnable ,long delay) {
        this.time = System.currentTimeMillis() + delay;//手动换算时间
        this.runnable = runnable;
    }
    
    public void run() {
        this.runnable.run();
    }
    public long getTime() {
        return time;
    }

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

那么我们就来具体实现schedule内的细节

    public MyTimer () {
        Thread t = new Thread (() -> {
           while(true) {
               //此时就要判断当前优先级任务队列的头任务是否到达执行时间了
               //如果到达则执行,不到达则循环判断
               if(queue.isEmpty()) {
                   continue;
               }
               long curTime = System.currentTimeMillis();
               MyTimeTask task = queue.peek();
               if(task.getTime() <= curTime) {
                   task.run();
                   queue.poll();
               }else{
                   //时间未到
                   continue;
               }
           }
        });
    }

但是此时可能存在不同线程同时修改同一个队列的情况,就要加入锁

public class MyTimer {
    /*
这里实际上最直观的是使用List
但是如果这里元素很多,就需要不停循环的扫描这里的任务,分别判定是否要执行
那么我们就可以将这些任务通过优先级队列保存起来,按照时间作为优先级队列的先后标准,就可以做到,队首元素就是最先要执行的任务,那么线程扫描的时候直接判断队首元素即可
那么我们就要对任务类实现comparable接口
     */
    PriorityQueue<MyTimeTask> queue = new PriorityQueue<>();
    private Object locker = new Object();
    public MyTimer () {
        Thread t = new Thread (() -> {
            synchronized (locker) {
                while(true) {
                    //此时就要判断当前优先级任务队列的头任务是否到达执行时间了
                    //如果到达则执行,不到达则循环判断
                    if(queue.isEmpty()) {
                        continue;
                    }
                    long curTime = System.currentTimeMillis();
                    MyTimeTask task = queue.peek();
                    if(task.getTime() <= curTime) {
                        task.run();
                        queue.poll();
                    }else{
                        //时间未到
                        continue;
                    }
                }
            }
        });
    }
    public void schedule(Runnable runnable,int delay) {
        synchronized (locker) {
            MyTimeTask timeTask = new MyTimeTask(runnable,delay);
            queue.offer(timeTask);
        }
    }
}

此时还存在两个比较核心的问题:

(1)上述的循环等待,实际上这个代码逻辑处于"忙等"的状态,确实是在等,但是等的过程中很忙,比如14:00要执行任务,但是13:00就开始等了

上述代码在短时间内疚会循环很多次,上述操作都是在"空转",一直在消耗cpu,没有真正执行任务

而我们要实现的就是在等待的时间内,要释放cpu资源

public class MyTimer {
    /*
这里实际上最直观的是使用List
但是如果这里元素很多,就需要不停循环的扫描这里的任务,分别判定是否要执行
那么我们就可以将这些任务通过优先级队列保存起来,按照时间作为优先级队列的先后标准,就可以做到,队首元素就是最先要执行的任务,那么线程扫描的时候直接判断队首元素即可
那么我们就要对任务类实现comparable接口
     */
    PriorityQueue<MyTimeTask> queue = new PriorityQueue<>();
    private Object locker = new Object();
    public MyTimer () {
        Thread t = new Thread (() -> {
            try {
                while(true) {
                    synchronized (locker) {
                        //此时就要判断当前优先级任务队列的头任务是否到达执行时间了
                        //如果到达则执行,不到达则循环判断
                        if(queue.isEmpty()) {
                            locker.wait();//队列为空,等待,直到有新的任务进来
                        }
                        long curTime = System.currentTimeMillis();
                        MyTimeTask task = queue.peek();
                        if(task.getTime() <= curTime) {
                            task.run();
                            queue.poll();
                        }else{
                            locker.wait(task.getTime() - curTime);
                            //时间未到,等待,直到有新的任务进来(判断新的任务是否要执行)
                            //或者时间到了,执行
                        }
                    }
                }
            }catch(InterruptedException e) {
                e.printStackTrace();
            }
        });
    }
    public void schedule(Runnable runnable,int delay) {
        synchronized (locker) {
            MyTimeTask timeTask = new MyTimeTask(runnable,delay);
            queue.offer(timeTask);
            locker.notify();
        }
    }
}

为什么这里不使用PriorityBlockingQueue呢??

实际上不如手动加锁,因为引入阻塞队列只能解决队列为空的阻塞,而时间没到的阻塞还是要我们自己去实现,还但是要引入新的锁,代码就搞复杂了,并且阻塞队列里面本来就有一把锁,这样反而可能导致死锁的出现

这里的wait能不能换成sleep?? ----不行!!!

notift唤醒wait,属于常规手段,是我们处理正常业务的流程,但是sleep通过interrupt唤醒,是处理异常业务的

此外,更加致命的是,wait休眠期间会释放锁,但是sleep可不会释放锁

感谢您的访问!!期待您的关注!!!

在这里插入图片描述

T04BF

🫵 小比特 大梦想

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

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

相关文章

羊大师解析,喝羊奶是热天喝还是凉天喝好?

羊大师解析&#xff0c;喝羊奶是热天喝还是凉天喝好&#xff1f; 在夏季&#xff0c;无论是热天还是相对凉爽的日子&#xff0c;适量喝羊奶都是有益的。羊奶的营养价值并不会因为天气温度的高低而有所改变&#xff0c;它始终是一个优质的蛋白质、矿物质和维生素来源。 然而&am…

【黑马点评Redis——002商户查询缓存】

1. 商户查询缓存 2. 知识储备和课程内容 2.1 什么是缓存 缓存是数据交换的缓冲区&#xff0c;是存贮数据的临时地方&#xff0c;一般读写性能较高。 浏览器缓存应用层缓存数据库缓存CPU缓存磁盘缓存 缓存的作用&#xff1a; 降低后端负载提高读写效率&#xff0c;降低响应…

鸿蒙开发实例:【配置OpenHarmony SDK】

配置OpenHarmony SDK 在设置OpenHarmony应用开发环境时&#xff0c;需要开发者在DevEco Studio中配置对应的SDK信息。 说明&#xff1a; 请注意&#xff0c;OpenHarmony SDK版本精简了部分工具链&#xff0c;因此不适用于HarmonyOS应用开发。 前提条件 已下载并安装好DevEco …

上位机图像处理和嵌入式模块部署(树莓派4b使用pcl点云库)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 图像处理&#xff0c;大家都知道它有显著的优点和缺点。优点就是分辨率高&#xff0c;信息丰富。缺点就是&#xff0c;整个图像本身没有深度信息。…

使用matlab/C语言/verilog分别生成coe文件

之前已经写过一个如何使用matlab生成coe文件&#xff0c;matlab自行运算生成三角波、正弦波等数据&#xff0c;并保存为COE文件。可跳转下面的网址进行查阅。 使用matlab生成正弦波、三角波、方波的COE文件_三角波文件.coe-CSDN博客https://blog.csdn.net/yindq1220/article/d…

深度学习——常用激活函数解析与对比

1、 简介 在神经网络中&#xff0c;激活函数扮演着至关重要的角色。它们的主要目的是引入非线性因素&#xff0c;使得网络能够学习和表示更加复杂的函数映射。以下是激活函数应具备的特点&#xff0c;以及这些特点为何重要的详细解释&#xff1a; 引入非线性有助于优化网络&am…

STM32标准库外部中断和定时器知识点总结

目录 前言 一、EXIT外部中断 &#xff08;1&#xff09;对射式红外传感器计次 &#xff08;2&#xff09;旋转编码器计次 二、TIM定时器 1.定时器定时中断 2.定时器外部时钟 3.TIM输出比较 &#xff08;1&#xff09;PWM驱动呼吸灯 &#xff08;2&#xff09;PWM驱动舵…

如何搭建一个vue2组件库(king-ui-pro)

文章引用图片无法查看&#xff0c;直接查看原文 感兴趣的可以关注或订阅下这个系列&#xff0c;后续会陆续将相关的组件与公共方法进行分享 目前已经完成了的组件有 多行省略pro版&#xff0c;不是简单的多行省略效果 公共方法&#xff1a; 1、图片预览&#xff0c;知乎的图…

STL-vector的使用及其模拟实现

在C中&#xff0c;vector是标准模板库&#xff08;STL&#xff09;中的一种动态数组容器&#xff0c;它可以存储任意类型的元素&#xff0c;并且能够自动调整大小。vector提供了许多方便的成员函数&#xff0c;使得对数组的操作更加简单和高效。 vector的使用 vector的构造函数…

elementUi 日期选择器 组件禁止手输

添加:editable"false" <el-date-pickerv-model"formInline.EndTime"type"datetime"placeholder"选择结束时间"format"YYYY-MM-DD HH:mm:ss"value-format"YYYY-MM-DD HH:mm:ss":editable"false">&…

AI大模型量化格式介绍(GPTQ,GGML,GGUF,FP16/INT8/INT4)

在 HuggingFace 上下载模型时&#xff0c;经常会看到模型的名称会带有fp16、GPTQ&#xff0c;GGML等字样&#xff0c;对不熟悉模型量化的同学来说&#xff0c;这些字样可能会让人摸不着头脑&#xff0c;我开始也是一头雾水&#xff0c;后来通过查阅资料&#xff0c;总算有了一些…

通用变频器ACS800-04M-0320-3可议价

商业别名&#xff1a;ACS800-04M-0320-3 产品编号&#xff1a;68279429 ABB 型号名称&#xff1a;ACS800-04M-0320-3 目录说明&#xff1a;ACS800-04M-0320-3&#xff1b; ACS800-04M-0320-3 Pcont.max:250kW, Icont.max:521A 原产地&#xff1a;芬兰 (FI) 海关税则号&#xf…

# 从浅入深 学习 SpringCloud 微服务架构(二)模拟微服务环境(2)通过 RestTemplate 调用远程服务

从浅入深 学习 SpringCloud 微服务架构&#xff08;二&#xff09;模拟微服务环境&#xff08;2&#xff09;通过 RestTemplate 调用远程服务 段子手168 1、打开 idea 创建父工程 创建 artifactId 名为 spring_cloud_demo 的 maven 工程。 --> idea --> File -->…

client-go源码结构及客户端对象

一、基础知识介绍 1、GVR 和 GVK G Goup资源组&#xff0c;包含一组资源操作的集合VVersion资源版本&#xff0c;用于区分不同API的稳定程度及兼容性RResource资源信息&#xff0c;用于区分不同的资源APIKKind资源对象类型&#xff0c;每个资源对象都需要Kind来区分它自身代表…

老化测试电源作用及选购标准

老化测试电源作用及选购标准 为了保证电子产品的稳定性和可靠性&#xff0c;我们需要对产品进行老化测试。老化测试电源是一种专门用于测试电子元器件、电源模块等产品在长时间、持续负载工作状态下稳定性和可靠性的电源设备&#xff0c;也被称为“测试电源”、“老化电源”等。…

【Linux】进程的程序地址空间①

目录 前言&#xff1a; 1.什么是地址空间 区域划分 页表&#xff1a; 2.为什么要有地址空间 2.1 进程与内存解耦合 2.2安全 3.凭什么说进程具有独立性&#xff1a; 4.用地址空间解释一下申请内存 前言&#xff1a; 在C语言中&#xff0c;我们说我们将内存分为&#xff0c;栈区…

【目标跟踪】ByteTrack详解与代码细节

文章目录 一、前言二、代码详解2.1、新起航迹2.2、预测2.3、匹配2.4、结果发布2.5、总结 三、流程图四、部署 一、前言 论文地址&#xff1a;https://arxiv.org/pdf/2110.06864.pdf git地址&#xff1a;https://github.com/ifzhang/ByteTrack ByteTrack 在是在 2021 年 10 月…

同元软控专业模型库系列——热流篇

一、引言 传热与流动是自然界与科学技术领域最普遍的物理现象。聚焦工业领域&#xff0c;传热、流体流动和燃烧问题是热工、核能、动力机械等行业所需研究解决的主要问题。复杂热流系统往往具有高复杂性、高成本性和高可靠性的特点&#xff0c;传统研制模式已逐渐无法满足现有…

【UE5.1 C++】提升编译速度

步骤 1. 在“C:\Users\用户\AppData\Roaming\Unreal Engine\UnrealBuildTool”目录下找到“BuildConfiguration.xml”文件 打开“BuildConfiguration.xml”&#xff0c;添加如下部分内容 <?xml version"1.0" encoding"utf-8" ?> <Configuratio…

干货:40个数据统计和分析的术语,让你的可视化大屏有理有据

1. 总体&#xff08;Population&#xff09;&#xff1a;指研究对象的全体&#xff0c;即研究问题所涉及的所有个体或事物的集合。 2. 样本&#xff08;Sample&#xff09;&#xff1a;从总体中选取的一部分个体或事物&#xff0c;用于代表总体进行研究。 3. 参数&#xff08…