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

news2024/11/15 17:30:36

在这里插入图片描述

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

🫵 小比特 大梦想

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

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

相关文章

计算机网络3——数据链路层5高速以太网

文章目录 一、100BASE-T 以太网二、吉比特以太网三、10吉比特以太网(10GbE)和更快的以太网四、使用以太网进行宽带接入 随着电子技术的发展&#xff0c;以太网的速率也不断提升。从传统的10Mbits以太网一直发展到现在常用的速率为1Gbits的吉比特以太网&#xff0c;甚至更快的以…

深度剖析图像处理—边缘检测

什么是边缘检测 边缘检测(Edge Detection)就是提取图像中的边缘点(Edge Point)。边缘点是与周围像素相比灰度值有阶跃变化或屋顶状变化的像素。边缘常存在于目标与背景之间、目标与目标之间、目标与其影子之间。 ​ 在图像处理和图像分析中&#xff0c;经常要用到边缘(Edge)、边…

【数学归纳法 反证法】菲蜀定理

裴蜀定理&#xff08;或贝祖定理&#xff0c;Bzout’s identity&#xff09;得名于法国数学家艾蒂安裴蜀&#xff0c;说明了对任何整数a、b和它们的最大公约 数d&#xff0c;关于未知数x和y的线性不定方程&#xff08;称为裴蜀等式&#xff09;&#xff1a;若a,b是整数,且&…

绿联 安装transmission

绿联 安装transmission及中文UI 1、镜像 linuxserver/transmission:latest 2、安装 2.1、创建容器 按需配置权重。 2.2、基础设置 2.3、网络 桥接即可。 注&#xff1a;如果使用IPV6&#xff0c;请选择"host"模式。 注&#xff1a;如果使用IPV6&#xff0c;请选…

Ts支持哪些类型和类型运算(下)

目录 1、条件判断 &#xff08;extends &#xff1f;&#xff09; 2、推导 infer 3、联合 | 4、交叉 & 5、映射类型 1、条件判断 &#xff08;extends &#xff1f;&#xff09; ts里的条件判断&#xff0c;语法为 T extends XXX ? true : false &#xff0c;叫做…

vulfocus靶场tomcat-cve_2017_12615 文件上传

7.0.0-7.0.81 影响版本 Windows上的Apache Tomcat如果开启PUT方法(默认关闭)&#xff0c;则存在此漏洞&#xff0c;攻击者可以利用该漏洞上传JSP文件&#xff0c;从而导致远程代码执行。 Tomcat 是一个小型的轻量级应用服务器&#xff0c;在中小型系统和并发访问用户不是很多…

软考 系统架构设计师系列知识点之大数据设计理论与实践(17)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之大数据设计理论与实践&#xff08;16&#xff09; 所属章节&#xff1a; 第19章. 大数据架构设计理论与实践 第5节 Lambda架构与Kappa架构的对比和设计选择 19.5.1 Lambda架构与Kappa架构的特性对比 1. 复杂度与开发、…

智慧浪潮下的产业园区:洞察智慧化转型如何打造高效、绿色、安全的新园区

目录 一、引言 二、智慧化转型的内涵与价值 三、打造高效园区的智慧化策略 1、建设智能化基础设施 2、推广智能化应用 3、构建智慧化服务平台 四、实现绿色园区的智慧化途径 1、推动绿色能源应用 2、实施绿色建筑设计 3、加强环境监测与治理 五、保障园区安全的智慧…

Group Query Attention (GQA) 机制详解以及手动实现计算

Group Query Attention (GQA) 机制详解 1. GQA的定义 Grouped-Query Attention (GQA) 是对 Multi-Head Attention (MHA) 和 Multi-Query Attention (MQA) 的扩展。通过提供计算效率和模型表达能力之间的灵活权衡&#xff0c;实现了查询头的分组。GQA将查询头分成了G个组&#…

一文学会Amazon transit GateWay

这是一个中转网关&#xff0c;使用时候需要在需要打通的VPC内创建一个挂载点&#xff0c;TGW会管理一张路由表来决定流量的转发到对应的挂载点上。本质上是EC2的请求路由到TGW&#xff0c;然后在查询TGW的路由表来再来决定下一跳&#xff0c;所以需要同时修改VPC 内子网的路由表…

ssm071北京集联软件科技有限公司信息管理系统+jsp

北京集联软件科技有限公司信息管理系统 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本信息管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理…

使用PlantUML绘制活动图、泳道图

最近在学PlantUML 太漂亮了 给大家欣赏一下 我也记录一下 startuml |使用前| start :用户打开旅游App; |#LightSkyBlue|使用后| :用户浏览旅游信息; |#AntiqueWhite|登机前| :用户办理登机手续; :系统生成登机牌; |使用前| :用户到达机场; |登机前| :用户通过安检; |#Light…

2024HVV在即| 最新漏洞CVE库(1.5W)与历史漏洞POC总结分享!

前言 也快到护网的时间了,每年的护网都是一场攻防实战的盛宴,那么漏洞库就是攻防红蓝双方人员的弹药库,红队人员可以通过工具进行监测是否存在历史漏洞方便快速打点,而蓝队则可以对资产进行梳理和监测历史漏洞,及时处理和修复,做好准备. 下面分享的…

发布自己的Docker镜像到DockerHub

学会了Dockerfile生成Docker image 之后&#xff0c;如何上传自己的镜像到 DockerHub呢&#xff1f;下面我以自己制作的 bs-cqhttp 镜像为例&#xff0c;演示一下如何将自己的镜像发布到 Docker 仓库。 1 生成自己的 Docker 镜像 1.1 实例镜像用到的文件 图1 实例镜像制作用到…

Web前端安全问题分类综合以及XSS、CSRF、SQL注入、DoS/DDoS攻击、会话劫持、点击劫持等详解,增强生产安全意识

前端安全问题是指发生在浏览器、单页面应用、Web页面等前端环境中的各类安全隐患。Web前端作为与用户直接交互的界面&#xff0c;其安全性问题直接关系到用户体验和数据安全。近年来&#xff0c;随着前端技术的快速发展&#xff0c;Web前端安全问题也日益凸显。因此&#xff0c…

注意libaudioProcess.so和libdevice.a是不一样的,一个是动态链接,一个是静态

libaudioProcess.so是动态链接&#xff0c;修改需要改根文件系统&#xff0c;需要bsp重新配置 libdevice.a是静态链接&#xff0c;直接替换就行 动态链接文件修改 然后执行fw_update.sh

HarmonyOS ArkUI实战开发-手势密码(PatternLock)

ArkUI开发框架提供了图案密码锁 PatternLock 组件&#xff0c;它以宫格图案的方式输入密码&#xff0c;用于密码验证&#xff0c;本节读者简单介绍一下该控件的使用。 PatternLock定义介绍 interface PatternLockInterface {(controller?: PatternLockController): PatternL…

3D MINS 多模态影像导航系统

3D MINS多模态影像导航系统&#xff08;Multimodal Image Navigation System&#xff09;是SunyaTech研发的建立在DICOM&#xff08;Digital Imaging and Communications in Medicine&#xff09;图像基础之上的多模态影像导航系统&#xff0c;集二维影像PACS管理、三维影像层级…

shell进阶之正则表达式:字符转义(十七)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

NovaMSS音乐源分离v1.3.3社区版

软件介绍 NovaMSS 基于最新 AI 模型优化的音乐源分离工具。它能够轻松地批量提取伴奏、人声、贝斯、鼓点等音轨&#xff0c;并且支持 GPU 加速&#xff0c;以提高处理速度和效率。社区版完全免费&#xff0c;简单易用&#xff0c;上传文件&#xff0c;点击处理&#xff0c;查看…