Java 多线程6——计时器Timer的使用 + 详细代码模拟实现 + 代码优化

这篇具有很好参考价值的文章主要介绍了Java 多线程6——计时器Timer的使用 + 详细代码模拟实现 + 代码优化。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

本人是一个刚刚上路的IT新兵,菜鸟!分享一点自己的见解,如果有错误的地方欢迎各位大佬莅临指导,如果这篇文章可以帮助到你,劳请大家点赞转发支持一下!

本篇文章为大家带来的仍然是多线程编程,计时器是许多场景都会应用到的一个非常方便快捷实用的类。


一、定时器是什么?

🦉定时器,顾名思义他的功能类似于一个闹钟,但又比闹钟更加智能便捷。

🎗️在手机上,你可以设置某个时间的闹钟,并备注上内容,以便提醒自己这个时间要干什么。

🧑🏻‍💻 编程里的定时器,也是如此,他需要你先设置"需要执行的代码",到了这个时间程序就会自动帮你执行这一段代码。

换句话说, 定时器就是属于你的超级免费劳动力,你给他布置什么时间去执行什么任务,时间到了他就会自动去执行对应的任务。


二、定时器如何使用

标准库提供了一个Timer类,这个类就是计时器。

而像里面添加闹钟的方法是schedule方法 ,这也是该类的核心方法。


java计时器,JavaEE,java,java-ee
java计时器,JavaEE,java,java-ee
java计时器,JavaEE,java,java-ee


1️⃣ 第一个参数:TimerTask类型的task,这个参数实际上与一个线程无异,所以要重写run方法

2️⃣ 第二个参数:long类型的delay,就是延迟执行的时间。意思就是多少ms后执行第一个参数这个线程。 单位:ms


    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("3000ms后");
            }
        },3000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("2000ms后");
            }
        },2000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("1000ms后");
            }
        },1000);
    }

java计时器,JavaEE,java,java-ee


🎪 可以看到,三个任务都执行完以后,程序并没有结束,是因为Timer类内部有一个前台线程,会阻止进程结束。


三、代码模拟实现定时器

1.理论准备

java计时器,JavaEE,java,java-ee
Timer内部有两个核心的成员:
1️⃣ TaskQueue类型的queue。
2️⃣ TimerThread类型的thread。


thread是一个Timer内置的前台线程,他会去按时执行定时器中的任务。
java计时器,JavaEE,java,java-ee
java计时器,JavaEE,java,java-ee
核心方法是 使用while循环让他按时去执行计时器中已有的任务与后续添加的任务

这也就是为什么上面,计时器中所有任务都执行完毕,进程仍没有结束的原因。

所以咱们自己写一个定时器, 也需要一个线程去按时执行任务,并且不确定后续会不会有新添加的任务,所以在线程里也要搞一个死循环


queue是用来存放线程任务的队列,而 定时器的优先级不是先来后到,而是基于时间快慢

就比如:

你想一小时后吃早饭,定了一个🍙吃饭闹钟。
你又想半小时后起床,定了一个🛏️起床闹钟。
那么起床闹钟一定比吃饭闹钟先响!

所以存储任务的数据结构我们选择使用一个优先级队列。因为涉及到了线程,我们就 使用一个线程安全的带有优先级的阻塞队列PriorityBlockingQueue


上述两个内容都了解之后,我们还需要一个类来 存储任务 延迟的时间 。与schedule方法中的TimerTask类似

该类需要两个成员变量:

1️⃣一个Runnable类型来存放任务。
2️⃣一个long类型来确定执行时间,以便放入队列中。(六点的两小时后 == 七点的一小时后。)存放该任务的时间+延迟执行的时间==该任务的执行时间


2.代码实现

任务类

class MyTask implements Comparable<MyTask>{
    // 存储任务
    public Runnable runnable;

    // 延迟的时间
    public long time;

    public MyTask(Runnable runnable, long dely) {
        this.runnable = runnable;
        //获取当前时间戳并+dely,作为当前任务实际执行的时间戳
        this.time = dely + System.currentTimeMillis();
    }

    // 重写compareTo方法,让先执行的任务在优先级阻塞队列的队首
    @Override
    public int compareTo(MyTask o) {
        return (int)(this.time - o.time);
    }
}

计时器

public class MyTimer {

    // 创建优先级阻塞队列,计时器的核心数据结构
    private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();

    public void schedule(Runnable runnable,long delay) {
        // 创建任务
        MyTask myTask = new MyTask(runnable,delay);
        // 将任务放进计时器
        queue.put(myTask);
    }

    public MyTimer() {
        // 内置线程执行计时器中的任务,并等待添加任务
        Thread thread = new Thread(() -> {
            while (true) {
                try {
                    // 取出计时器中最优先执行的任务
                    // 优先级阻塞队列,不用担心队列为空抛出异常
                    // 队列为空则会阻塞等待
                    MyTask myTask = queue.take();
                    // 判断是否到达执行时间
                    if(System.currentTimeMillis() >= myTask.time) {
                        // 到达执行
                        myTask.runnable.run();
                    } else {
                        // 未到达再放回计时器
                        queue.put(myTask);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
    }
}

运行结果:
java计时器,JavaEE,java,java-ee


3.🧑🏻‍💻优化代码

上述的代码运行结果正确,没有什么问题了。
🧑🏻‍💻🧑🏻‍💻🧑🏻‍💻但是还可以进行优化。


忙等问题
如果这个队列里最先执行的任务也要一个小时后才执行,也就是说程序在这一个小时中就要一直重复 取出任务 判断时间 放回任务 这一系列操作,期间会一直占用CPU资源。

CPU的执行效率之高,执行速度之快,一秒钟可以执行上亿次语句,那么忙等问题就浪费了天文数字量级的CPU资源

💉💉💉那么如何解决这个忙等问题呢。
很简单,只需要使用前面分享过的 wait方法


但是就会出现一个新的问题:

还是上述情景, 添加了wait方法后,应该是取出这个任务后,让当前线程等待一小时,这期间不再占用CPU资源

如果我又插入了一个十分钟后就需要执行的任务呢??
那么此时线程还在等待中,不会被CPU调度执行,那么这个十分钟后执行的任务就无法被及时执行。

💉💉💉因此每次插入一个新的任务都要用 notify方法 来唤醒线程。

优化后代码:


public class MyTimer {

    // 锁对象
    Object locker = new Object();

    // 创建优先级阻塞队列,计时器的核心数据结构
    private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();

    public void schedule(Runnable runnable,long delay) {
        // 创建任务
        MyTask myTask = new MyTask(runnable,delay);
        // 将任务放进计时器
        queue.put(myTask);
        synchronized (locker) {
        	// 叫醒线程
            locker.notify();
        }
    }

    public MyTimer() {
        // 内置线程重复执行计时器中的任务
        Thread thread = new Thread(() -> {
            while (true) {
                try {
                    synchronized (locker) {
                        // 取出计时器中最优先执行的任务
                        MyTask myTask = queue.take();
                        // 判断是否到达执行时间
                        if (System.currentTimeMillis() >= myTask.time) {
                            // 到达执行
                            myTask.runnable.run();
                        } else {
                            // 未到达再放回计时器
                            queue.put(myTask);
                            // 让线程等待相应时间
                            locker.wait(myTask.time - System.currentTimeMillis());
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
    }
}

总结

以上就是今天要分享的内容了,本文介绍了定时器是什么,原理,以及代码模拟实现,定时器也是多线程编程中常常用的工具,希望大家都能掌握。

路漫漫不止修身,也养性。文章来源地址https://www.toymoban.com/news/detail-764012.html

到了这里,关于Java 多线程6——计时器Timer的使用 + 详细代码模拟实现 + 代码优化的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Java EE初阶八】多线程案例(计时器模型)

            计时器类似闹钟,有定时的功能,其主要是到时间就会执行某一操作,即可以指定时间,去执行某一逻辑(某一代码)。         在java标准库中,提供了Timer类,Timer类的核心方法是schedule( 里面包含两个参数,一个是要执行的任务代码,一个是设置多久之后

    2024年01月21日
    浏览(39)
  • java计时器

      在 Java中,我们有一个重要的概念:同步和异步。同步就是 Java中的线程安全,异步就是 Java中的线程非安全。 在使用 JVM时,我们一般都是用 start ()方法启动一个线程,然后设置时间,比如定时器,定时器是在某个指定的时间执行相应的任务。但是,在实际应用中,我们

    2023年04月18日
    浏览(48)
  • 使用 JavaScript 创建一个兔年春节倒数计时器

    💂 个人网站:【 海拥】【神级代码资源网站】【办公神器】 🤟 基于Web端打造的:👉轻量化工具创作平台 💅 想寻找共同学习交流的小伙伴,请点击【全栈技术交流群】 给大家安利一个免费且实用的轻量化工具创作平台,👉点击跳转到网站。 我们可以通过多种方式构建

    2024年01月23日
    浏览(42)
  • UE4 计时器的简单使用(FTimerManager)

    我在UE中开发视频播放插件时,需要实现UE的媒体框架。 当引擎Tick到MediaModule时,会从我自己实现的Player中取视频帧数据,然后用其初始化成纹理并输送到渲染管线中。 然而我使用的SDK没有视频取帧的回调函数,只能自己在外部控制取帧速率,即帧号的递增时机: 要么每次跟

    2023年04月08日
    浏览(27)
  • Unreal Engine11:触发器和计时器的使用

    主要是介绍一下触发器和计时器的使用; 1. 新建一个C++类 创建的C++类也是放在Source文件夹中的Public和Private文件夹中; 选择Actor作为继承的父类; 头文件包括一个触发器和两个静态网格,它们共同组成一个Actor,定义如下: cpp 文件的构造函数实现如下: 一些注意的点如下:

    2024年02月03日
    浏览(49)
  • 【嵌入式】STM32计时器编码器接口模式使用

    使用STM32调试电机或传感器时经常会使用到计数器的编码器接口模式,本文主要记录该模式的固件库配置方法,并给出使用该模式获取光栅测距值的实例。 硬件: STM32F103C8T6 光栅测距传感器 编码器接口模式为STM32计时器的一种特殊使用模式,该模式下可对编码器输出的脉冲信

    2024年02月20日
    浏览(37)
  • Android 计时器Chronometer 使用及源码分析(1),android音视频框架

    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_textview_chronometer);//加载布局文件 initView(); } private void initView() { btn_start = findViewById(R.id.btn_start); btn_stop = findViewById(R.id.btn_stop); btn_reset = findViewById(R.id.btn_reset); chronome

    2024年04月14日
    浏览(38)
  • 14、计时器、定时器设计与应用

    掌握同步四位二进制计数器 74LS161 的工作原理和设计方法 掌握时钟/定时器的工作原理与设计方法 任务 1:采用行为描述设计同步四位二进制计数器 74LS161 任务 2:基于 74LS161 设计时钟应用 1.创建工程并创建 Verilog 文件 建立 HDL 类型的工程 My74LS161,创建 Verilog 文件 My74LS161,

    2024年02月03日
    浏览(41)
  • 51单片机通过计时器实现倒计时

    软件 : Keil5+Proteus7 元件 : AT89C51 * 1,7SEG-MPX2-CA * 1

    2024年02月16日
    浏览(49)
  • Qt实现计时器

    一、样图 二、代码 mainwidow.h mainwindow.cpp main.cpp ui_mainwindow.h

    2024年02月07日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包