多线程2(定时器的实现)

Yii2的异步多线程服务之swoole 欢迎使用Markdown编辑器写博客本Markdown编辑器使用StackEdit修改而来,用它写博客,将会带来全新的体验哦: Markdown和扩展Markdown简洁的语法 代码块高亮 图片链接和图片上传 LaTex数学公式 UML序列图和流程图 离线写博客 导入导出Markdown文件 丰富的快捷键 快捷键 加粗 Ctrl + B 斜体 Ctrl + I 引用 Ctrl 阅读详情

在前面的一张我们以及聊了我们为什么要使用线程,线程的好处,以及线程怎么使用,最后就是我们的重点线程安全,兄弟们可以去复习一下。因为这篇我们就是有了前面的基础再实现定时器的。(完整的代码都在本章最下面)

多线程从认识到理解到使用(Java)-优快云博客

定时器

作用

 那废话不多说,现在开始聊我们的定时器,我们先想想定时器有什么作用,先知道有什么作用了之后才可以理解并且写出来吧,普通的定时器就是到点就响,当然这个没有什么写的必要,和多线程碰不到边,所以我们现在要聊的是,定时器pro,我们给计算机一个任务,再告诉计算机什么时候执行,让计算机到点了就自己执行,当然一个有点少,所以我们可以创建多个任务让计算机,自己根据时间来执行相关的任务

实现

既然我们要给计算机多个任务,所以我们就需要使用一个数据结构来存储这些数据,那我们要使用什么数据结构呢,就需要看我们需要的数据结构具体要实现什么功能,那你想想我们想要定时器存储的数据实现什么呢,对,就是根据时间的先后输出任务就好了,所以,我们的数据结构就想要一个可以输出时间最早的任务,而要最早时间的数据结构,有那个符合啊,想到最,我们就应该可以想到堆(优先级队列),所以我们就要先创建一个优先级队列,来存储我们就任务和任务的执行时间,但是我们的优先级队列只能存储一个类型啊,不可以同时存储任务和任务的时间啊,所以我们就需要把任务和任务时间包装成一个类不就好了吗。并且写上构造方法

但是你想想假如领导叫我们半个小时之后去帮他拿快递,我们就这样记下来,半小时后拿快递,但是现在的半个小时,具体是什么时候(现在的半个小时,过5分钟就不是半个小时了),所以这样的时间计数就是不清楚的所以我们会写(假如现在2点半)3点帮领导拿快递,就以绝对时间来记录。所以我们的代码就改为

我们在写上Get和Set方法就好了。我们就把一个任务处理好了,就开始写定时器的主体了。

上面说了需要一个堆(优先级队列)这样的数据结构来存储数据,所以我们就先创建

然后我们就需要一个线程来实现定时器的运用逻辑,所以我们先创建一个线程

想想,定时器的主体逻辑怎么实现,是不是需要一个循环(这个循环要一直进行,因为我们随时会向堆里面加任务),在这个堆上面循环取出任务时间最早的任务,开始执行就好了,那就有两中可能,如果这个堆里面是空,我们怎么办,就continue就好了(后续我们会有优化手段),一种可能就是堆不为空了,那我们就需要判断一下这个任务的执行时间到了没有(那为什么我们不需要判断其他的任务执行时间好了没有啊,因为我们这个是优先级队列,而且是以时间来比较,所以我们现在就可以来写比较方法了)

而这个什么时候用this - o ,什么时候用o - this,你不需要理解,你自己写的时候,都试一试是性价比最高的。好,还是上文聊的判断这个任务的执行时间到了没有,如果到了,我们就把这个任务跑起来就好了,对了,还要把这个数据从堆里面移除哦

另一种情况就是任务的执行时间还没有到,就和上面一样continue咯,最后就是线程开始就好了。

所以我们就差最后一步了,就是把任务添加到堆(优先级队列)中就好了,这个比较简单,就是

我们的定时器就好了

我们先看看全部代码,这个是参考,后面还有改进的版本

import java.util.Comparator;
import java.util.PriorityQueue;

public class MainB {
    public static void main(String[] args){
        MyTime myTime = new MyTime();
        for(int i = 0;i<10;i++){
            int j = i;
            myTime.Schedule(new Runnable(){
                @Override
                public void run(){
                    System.out.println("定时任务 100  "+j);
                }
            },100);
            myTime.Schedule(new Runnable(){
                @Override
                public void run(){
                    System.out.println("定时任务 500  "+j);
                }
            },500);

        }
    }
}
class MyTask implements Comparable<MyTask>{
    @Override
    public int compareTo(MyTask o){
        return (int) (this.GetTime() - o.GetTime());
    }
    private Runnable runnable;
    private long time;
    MyTask(Runnable runnable,long time){
        this.runnable = runnable;
        this.time = time + System.currentTimeMillis();
    }
    public Runnable GetRunnable(){
        return runnable;
    }
    public long GetTime(){
        return time;
    }

}
class MyTime  {
    public void Schedule(Runnable runnable,long time){
        MyTask myTask1 = new MyTask(runnable,time);
        queue.offer(myTask1);
        }
    private  PriorityQueue<MyTask> queue = new PriorityQueue<>();
    public MyTime(){
        Thread thread = new Thread(()->{
            while (true){
                if(queue.isEmpty()){
                    continue;
                }
                MyTask myTask = queue.peek();
                long NowTime = System.currentTimeMillis();
                if(myTask.GetTime() > NowTime){
                    myTask.GetRunnable().run();
                    queue.poll();
                }else {
                    continue;
                }
                
            }
        });
        thread.start();
    }

}

线程安全

你看上面的代码,你觉得有没有问题(问了就肯定有),对,那你可以想到哪里有问题不。

如果你连线程安全有哪些都想不起来,就把这篇翻到最上面,那里有一个链接点进去。

线程主要有两种(可重入不算因为我们使用的锁synchronized就是可重入锁)一是,线程的随机调度和可见性问题,因为我们这里没有触发可见性的变量,所以我可以告诉你,这里的线程问题就是随机的调度问题,这样你在来想想这个代码有那里要改,线程随机调度说到底就是,我们在修改一个变量的时候,不希望同时有操作也在修改,所以我们在这里避免一下,加任务和读出任务就好了,所以我们在Schedule和MyTime中加synchronized就好了,但是这个其实还有一点需要优化的地方,前面埋了铺垫哦。就是continue的地方,现在我们在这个地方加了锁,我们还一直continue,是不是就是有点占着茅坑不拉屎感觉,我拿着锁,就是不用,是不是,就都导致后面我要Schedule使用不了,所以说这里我们需要优化,但是怎么优化呢,特别简单,就是使用wait(这个就是休眠一直到计算机使用notify来唤醒他,而我们有两个continue的地方,第一个在

我们判断这个堆(优先级队列)是不是空的时候,这里直接变为空就好了。我们就在Schedule使用的时候,加一个notify就可以了。

第二个在

我们要执行的任务时间还没有到,所以我们要wait,但是我们在加任务的时候会notify会不会把这个唤醒了啊,当然是会的,但是你想这个程序醒来了,我继续向下走就是while(true)又回到开头了(假如新任务时间是最早的,刚刚好就判断新任务时间到了没有,)老任务不过是走到执行时间没有到的位置继续wait,但是我这里wait就没有计算机唤醒我了,所以我需要直接醒来,我们就可以给wait设置一个睡眠最大时间就是要唤醒的时间减当前时间,就好了,定时器就写好了。

我们就来聊聊,一个小问题,我给wait设置一个睡眠的最大时间,而不唤醒,不就是sleep吗。我们从使用上面来看其实是这样的,但是我们使用wait的初衷就是为了去唤醒他,我们设置一个最大值只是因为防范于未来(就是一直不唤醒,从而带来的影响)。

总结

定时器虽然代码不多,但是每一个地方都是需要考虑的,所以写一个定时器可以对自己的多线程相当于一个实践一样,同时看看这个多线程有没有那里没有学懂。下面就是全部的代码了

import java.util.PriorityQueue;

class MyTimerTask implements Comparable<MyTimerTask>{//这个是定时器的一个任务
    @Override
    public int compareTo(MyTimerTask o){
        return(int)(this.delay - o.delay);
    }
    private Runnable task;
    private long delay;
    public MyTimerTask(Runnable task , long delay){
        this.delay = System.currentTimeMillis() + delay;
        this.task = task;
    }
    public Runnable GetTask(){
        return task;
    }
    public long GetTime(){
        return delay;
    }
}
class MyTimer{//这个是定时器的主体
    private Object locker = new Object();
    private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
    public MyTimer(){
        Thread t = new Thread(() -> {
            while (true) {
                synchronized (locker) {
                try {
                    if (queue.isEmpty()) {
                        continue;
                    }//下面就是不为空
                    MyTimerTask task = queue.peek();
                    long NowTime = System.currentTimeMillis();
                    if (NowTime < task.GetTime()) {
                        //不执行
                        locker.wait(task.GetTime() - NowTime);
                    } else {
                        //执行
                        task.GetTask().run();
                        queue.poll();
                    }
                } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            });
        t.start();
    }
    public void schedule(Runnable task, long delay){
        synchronized (locker){
        locker.notify();
        MyTimerTask myTimerTask = new MyTimerTask(task,delay);
        queue.offer(myTimerTask);
        }
    }
}
public class Main {
    public static void main(String[] args){
        MyTimer timer = new MyTimer();
        for(int i = 0;i<10;i++){
            int j = i;
        timer.schedule(new Runnable(){
            @Override
            public void run(){
                System.out.println("定时任务 500  "+j);
            }
        },500);
        timer.schedule(new Runnable(){
            @Override
            public void run(){
                System.out.println("定时任务 100  "+j);
            }
        },100);
        }
    }
}

RT-Thread HWTIMER实战:用硬件定时器TIM2实现一个精准的软件看门狗 本文详细介绍了如何利用RT-Thread的HWTIMER设备驱动和STM32的TIM2硬件定时器构建一个高精度的软件看门狗系统。通过硬件定时器的独立时钟源和纳秒级精度,解决了传统软件定时器在系统高负载下的调度延迟问题,实现了微秒级精度的线程监护,显著提升嵌入式系统的可靠性。 阅读详情

相关推荐

C++多线程定时器完整实现

头文件: #pragma once #include #include #include //回调接口 typedef struct ITimerEngineCallBack { virtual bool OnTimerEngineCallBack( DWORD TimerID, DWORD Param ) = 0; }* ITimerEngineCallBackPtr; clas

F_r_e_e_x的专栏 1万+

【多线程案例】Java实现简单定时器(Timer)

在日常生活中,如果我们想要在 t 时间 后去做一件重要的事情,那么为了防止忘记,我们就可以使用闹钟的计时器功能,它会在t时间后执行任务(响铃)提醒我们去执行这件事情. — 这就是Java定时器的简单功能。它作为一种日常开发组件。约定一个时间,时间到达之后,执行某个任务。常被用于网络通信。 ​

m0_73381672的博客 790

基于Qt 5.14.2的QTimer定时器使用联合HALCON调用Balser相机

基于Qt 5.14.2的QTimer定时器使用联合HALCON调用Balser相机

NCUTer 835

多线程定时任务(线程池的创建)

一、概述 New Thread的弊端如下:        a、每次New Thread新建对象性能差。        b、线程缺乏统一的管理,可能无限制的新建线程,相互之间竞争,极可能占用过多的系统资源导致死机 或者 OOM。        c、缺乏更多功能,如定时执行、定期执行、线程中断。 Java提供的四种线程池的好处在于:        a、重用存在的线程,减少对象创建、消亡的开销,...

Sum 1万+

深入浅出ASAP2标准:手把手教你编写和解析A2L文件(含Python示例代码)

本文深入解析ASAP2标准下的A2L文件,涵盖基础概念、文件结构及Python实现代码,帮助开发者掌握汽车ECU标定和测量中的关键数据管理技术。通过详细示例,展示如何编写和解析A2L文件,提升电控数据管理效率。

happy2的博客 1099

RimWorld Mod开发避坑指南:从Defs命名规范到XML结构详解(附实战代码)

本文深入解析RimWorld Mod开发中Defs文件的编写规范与高级技巧,涵盖命名冲突预防、XML结构优化及高级功能实现。通过实战代码示例,帮助开发者避开常见陷阱,提升Mod兼容性与稳定性,特别适合RimWorld Mod开发者参考。

weixin_30790841的博客 450

使用定时器创建线程

使用定时器创建线程 public class Demo11 { public static void main(String[] args) { Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void...

zero_295813128的专栏 576

腾讯股票接口API(1)——根据股票代码获取详情

获取股票简要信息,找来找去也没有找到官方的文档,都是复制粘贴的,这里先简单记录一下。 1.简要信息 以贵州茅台为例: http://qt.gtimg.cn/q=s_sh600519 // sh600519是贵州茅台的股票代码,根据自己的关键字拼接URL 可以直接在Android项目中请求,会得到如下一串字符串: v_s_sh600519="1~贵州茅台~600519~2075.95~42.95~2.11~38930~794942~~26078.04~GP-A"; 这里v_s代表简要信息,sh600519

GeofferySun的专栏 6万+

SpringBoot 定时器和多线程(2)-定时器实现

springboot 创建定时器有两种方式一种是使用@EnableScheduling和@Scheduled注解,一种是使用SchedulingConfigurer配置,将定时任务创建出来。

myli92的博客 8419

告别‘抓瞎’:CANoe的Trace、Graphics和Logging窗口实战技巧,高效分析总线故障

本文深入解析CANoe的Trace、Graphics和Logging窗口实战技巧,帮助工程师高效分析总线故障。通过智能过滤、可视化监控和智能日志管理,构建系统级问题定位工作流,显著提升车载网络故障排查效率。

weixin_42709044的博客 292

TMS320F28377D的sys/bios双核工程配置详解——从零搭建到RAM优化

本文详细解析了TMS320F28377D双核系统在sys/bios环境下的工程配置与RAM优化策略。从开发环境搭建、SYS/BIOS工程创建到双核CMD文件配置,提供了实战技巧和常见问题解决方案,帮助开发者高效实现电力电子控制系统中的双核协作与性能优化。

weixin_28292761的博客 600

「JavaEE」多线程案例分析2:实现定时器

定时器类似一个闹钟,时间到了之后就会执行相应的任务Java 标准库中已经实现了一个定时器的类 Timer在定义好 timer 之后可以调用schedule把一个或多个任务(TimerTask)添加到定时器中@Override},2000);第一个参数就是任务内容,每个任务后面都会带有一个时间(第二个参数),这个时间是“相对时间”,是以 schedule 时的时间为基准,过了相对时间后才执行比如 2000ms,它表示调用 schedule 后再过 2000ms 就会执行这个任务。

Ice_Sugar_7的博客 1059

上位机介绍:C# 开发者的必知基础

上位机是指在工业控制系统中,位于上层、负责人机交互和数据管理的计算机/软件,与“下位机”(PLC、传感器、变频器、单片机等硬件设备)相对应,核心是“向下通信控制设备,向上展示交互数据”。下位机:一线干活的“工人”(执行采集、控制指令);上位机:指挥管理的“办公室人员”(发指令、看数据、做报表)。C#开发者掌握上位机开发的核心是“通信+UI+稳定性基础层:搞懂WinForms/WPF基础、串口/Modbus通信,能实现核心功能;进阶层:掌握分层设计、异步编程、工业级容错(断线重连、数据校验);

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1558

ROS2回调组及多线程实现

直接利用 C++ 标准库的 std::thread 创建独立线程,完全脱离 ROS2 的回调组机制。假设有一个 ROS2 节点,包含两个定时器回调函数,它们共享一个计数器 shared_data_,并分别在不同的线程中并发执行。假设两个定时器各运行 1 秒(1000 次回调),期望 shared_data_ 的最终值为 2000(每次回调增加 1)。使用 std::mutex pub_mutex_ 定义互斥锁的目的是为了确保 共享数据(如计数器)的线程安全。每次运行结果可能不同,且通常远小于预期值。

qq_53044655的博客 750

基于STM32设计的老人摔倒检测报警系统【升级版】(235)

在 `235 基于STM32设计的老人摔倒检测报警系统(OneNet+MQTT)` 项目基础上增加了心率、血氧、体温测量3个功能。

1508

Vue3 - 详细实现将多个文件批量导出为ZIP压缩包格式并下载功能,vue3将文件批量下载打包成ZIP下载保存本地,下载多个文件打包输出成zip格式(支持二进制文件流/base64图片/文件URL)

vue3,nuxt3,vue3批量导出文件打包成压缩包,Vue3中如何批量导出文件(图片/PDF)并打包压缩成ZIP,实现多张图片导出zip文件,vue3导出pdf文件很多如何变成导出一个zip文件,jszip插件文档流,vue3中下载多个文件打包为一个zip,Vue3中将文件批量下载打包成zip格式,批量下载图片并打包成压缩包,vue3导出多个pdf或excel打包成zip压缩包进行下载,批量下载文件并打压缩包,vue3中如何批量下载文件并生成zip格式文件,文件通过服务端接口返回二进制文件流如何处理,文

🏆 前端领域优质创作者 1万+

libevent多线程的正确使用

目的:让20个定时器(或者是网络socket)平均分配给三个线程,让每个线程监控若干个定时器超时事件。libevent版本:2.0.21-stable。操作系统:ubuntu16.04以上。

weixin_47342627的博客 1235

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

【富瀚微MC632X 软件定时器控制舵机周期运行】

在嵌入式系统开发中,定时器和舵机控制是两个非常基础但又十分重要的功能。本文将基于富瀚微MC632X平台,通过一个完整的实践demo,详细介绍如何使用软件定时器来控制SG90舵机实现周期性运动。

vor234的博客 836

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

【协程方式】实现【多任务调度 / 多线程】 仅需2byte的RAM 并带有详细视频讲解

本文介绍了一种基于协程方式实现多任务调度/多线程的技术方案。该方案仅需2字节RAM基础占用,每个任务额外占用6字节RAM,具有资源占用少、移植快捷的特点。通过软定时器实现非阻塞延时函数,并提供task_wait、task_for和task_while等控制函数来实现代码状态暂停和可控循环。方案避免了函数重入问题,保持了代码顺序执行的本质,同时提供了接近普通C语言的编程体验。文中包含底层原理详解、移植使用说明,并提供了基于AI8051U实验箱的示例代码。

czhaii的专栏 163

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Java多线程创建的五种常见方法

目录1. 继承Thread类2. 实现Runnable接口3. 定时器4. 继承Callable接口5. 基于线程池的方法最后 1. 继承Thread类 继承Thread并重写run方法,并调用start方法。 public class ThreadDemo extends Thread { @Override public void run() { while (true) { System.out.println(Thread.currentTh

技术宇宙 327

定时器的源码介绍与简单实现——多线程编程简单案例[多线程编程篇(5)]

在现代软件开发中,多线程编程能力已经成为程序员必须掌握的一项核心技能。随着计算机硬件的不断升级,单核CPU早已无法满足复杂应用的性能需求,多核并行运算已成为主流。而多线程正是实现资源最大化利用、任务高效并行执行的基础手段。而本博客是笔者多线程编程的第五篇博客!前面四篇都在下面的URL中,希望能得到大佬们的阅读和指点!!!多线程编程_callJJ的博客-优快云博客本期我们介绍的是定时器,然后翻看并且介绍它的源码。

cjejwe的博客 4029

PyQt/PySide2 中的 信号与槽 (pyqtSignal/Signal) 、多线程 (QThread) 和 定时器 (Timer)

文章目录写在前面PyQt5 和 PySide2 的区别信号与槽线程类定时器示例完整示例代码参考 写在前面 PyQt5 和 PySide2 的区别 他们背后原理的差别我就不细说了(我也不知道),你只要记住使用上基本差不多就行,网上搜索他们用法的时候,以哪个为关键词搜索都行吧,官网 给出了他们的差异,聚焦我们要讲的问题,在信号与槽机制和多线程机制上,他们的差别如下: # PyQt5 引入线程类和信号 ...

二月七 1万+

多线程——定时器

本篇文章会利用多线程的知识来编写一个代码案例——定时器,我会先介绍一下什么是定时器,再来介绍一下我们标准库中的定时器以及操作演示我们标准库中定时器的效果,最后我来手撕出来一个功能上与所演示标准库中定时器效果差不多的定时器,并对其实现的具体逻辑,在代码中进行详细介绍,那么就开始本篇文章介绍的内容吧。

HKJ_numb1的博客 1197

多线程代码案例之定时器

(1)“定时器”类似闹钟,在指定的时间会触发一定的操作(2)多线程中的定时器,是通过指定一个任务(Runnable),并且加上一个时间(例如:3000ms);这个任务不会立即被执行,而是会等待3000ms后才会开始执行(3)上面这种效果称为:延时执行/定时执行。

2301_77053417的博客 1252

C#多线程实现定时器

     我们在写winfrom时经常会用到定时器,比如客户端通过socket定时向服务器发送检测包等。.netframework提供了一个定时器控件System.Windows.Forms.Timer,使用很方便。     其实通过C#多线程也可以实现定时器功能。我的做法大致为:新开一个线程用于计时,当时间间隔过后用委托变量实现主窗体中的定时函数,再通过调用contro.invoke方法返回主窗体线程执行定时后的操作。     先定义两个类,timer类用于管理和配置定时器,interval_date用于

xfantasy的专栏 1万+

【多线程】定时器

本期内容主要介绍定时器,定时器类Timer的使用以及核心方法schedule(),并介绍如何自己实现一个定时器。

爱编程爱运动的博客 4678
上一篇: 多线程从认识到理解到使用(Java)
下一篇: 多线程3(单例模式和锁策略和synchronized的原理)
star050913
博客等级 码龄3年 277粉丝 33原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值