【Java | 多线程案例】定时器的实现

news2025/4/22 11:31:11

个人主页:兜里有颗棉花糖
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创
收录于专栏【Java系列专栏】【JaveEE学习专栏】
本专栏旨在分享学习JavaEE的一点学习心得,欢迎大家在评论区交流讨论💌

这里写目录标题

  • 一、Timer定时器
  • 二、Timer定时器的设计
  • 三、定时器的实现
  • 四、总结

一、Timer定时器

Java中,Timer类是用于计划和执行重复任务的类(Java标准库中确实提供了java.util.Timer类)。它可以在指定的时间间隔内重复执行一个任务,或者在指定时间点执行任务。

二、Timer定时器的设计

选择java.util包中的Timer类:
在这里插入图片描述

在这里插入图片描述
使用了Timer类的schedule()方法来安排一个任务在延迟3000毫秒后执行。在TimerTask的run()方法中,我们编写需要执行的具体任务逻辑
我们现在来了解一下TimerTask()这个抽象类(如下图):该类是一个抽象类,并且继承了Runnable方法创建了一个匿名内部类并实现了run()方法。这个匿名内部类可以被认为是继承了TimerTask抽象类,并提供了具体的实现代码。
在这里插入图片描述
调用timer.schedule()方法注册的任务,会由Timer内部的线程池去执行,而不是由调用schedule()方法的线程直接执行run()方法。
Timer类内部创建了一个线程池,用于执行注册的定时任务。当调用schedule()方法后,Timer会将传入的TimerTask对象添加到线程池中进行调度。

下面是一个简单的定时器程序,可以运行试试看:

import java.util.Timer;
import java.util.TimerTask;

public class Demo22 {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello world!!!");
            }
        },3000);
        System.out.println("程序开始执行喽!!!");
    }
}

运行结果如下:
在这里插入图片描述
可以看到程序并没有结束进程,原因如下:
Timer内部有自己的线程,为了保证随时处理新安排的任务,此线程会一直持续的执行,即此线程影响了阻止来整个进程的结束。

定时器是支持多个任务同时执行的,请看:
在这里插入图片描述
在这里插入图片描述

三、定时器的实现

代码实现如下:

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

class MyTimerTask implements Comparable<MyTimerTask> {
    private long time; // 表示任务什么时候开始执行
    private Runnable runnable; // 表示具体任务是啥

    public MyTimerTask(Runnable runnable,long delay) {
        // delay是一个相对的时间差
        time = System.currentTimeMillis() + delay;// 这里计算出任务执行的具体时间
        this.runnable = runnable;
    }

    public long getTime() {
        return time;
    }

    public Runnable getRunnable() {
        return runnable;
    }


    @Override
    public int compareTo(MyTimerTask o) {
        // 时间最少的元素放在队首,即时间越少优先级越高
        return (int)(this.time - o.time); // time是long类型
    }
}

// 这是定时器类的本体
class MyTimer {
    // 使用优先级队列来保存上面的N个任务
    private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
    // locker是用来加锁的对象
    private Object locker = new Object();

    // 定时器的核心方法,即把要执行的任务添加到队列中
    public void schedule(Runnable runnable,long delay) {
        synchronized (locker) {
            MyTimerTask task = new MyTimerTask(runnable,delay);
            queue.offer(task);
            // 每次来新的任务之后都会唤醒一下扫描线程,此时扫描线程就可以根据最新的任务情况来重新规划等待时间
            locker.notify();
        }
    }
    // MyTimer类中还需要一个扫描线程,一方面要负责检查队首元素是否是此时应该被执行的。
    // 另一方面,当任务到点开始执行之后,需要调用Runnable中的run方法来完成任务
    public MyTimer() {
        // 扫描线程
        Thread t = new Thread(() -> {
            while(true) {
                try {
                    synchronized(locker) {
                        while(queue.isEmpty()) {
                            // 队列为空时,此时不应该取这里的元素
                            locker.wait();
                        }
                        MyTimerTask task = queue.peek();
                        long curTime = System.currentTimeMillis();
                        if(curTime > task.getTime()) {
                            // 如果当前时间晚于任务的执行时间,就意味着我们要执行这个任务了
                            queue.poll();
                            task.getRunnable().run(); // 至此就可以执行该任务了
                        } else {
                            // 如果当前时间早于任务的执行时间,诶呀太早了,让这个线程(休眠)休息一会一会吧!!!
                            // Thread.sleep(task.getTime() - curTime);
                            locker.wait(task.getTime() - curTime);
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }

}

public class Demo23 {
    public static void main(String[] args) {
        MyTimer timer = new MyTimer();
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello world! 3");
            }
        },3000);
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello world! 2");
            }
        },2000);
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello world! 1");
            }
        },1000);
        System.out.println("程序开始执行!!!");
    }
}

运行结果如下:
在这里插入图片描述

四、总结

Timer类是Java中的定时工具类,它可以帮助我们实现在指定时间执行指定任务的功能。Timer类提供了一个方法即schedule方法,我们可以通过schedule方法来注册一个任务并指定执行该任务的时间,当执行时间到的时候,Timer类内部的线程就会负责调用执行注册的任务。

另外我们可以通过优先级队列的方式来实现类似于Timer类这样的定时器。

本文到这里就结束了,希望友友们可以支持一下一键三连哈。嗯,就到这里吧,再见啦!!!

在这里插入图片描述

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

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

相关文章

“推荐大战:抖音vs.快手”——背后的秘密全揭晓!

大家好&#xff0c;我是小米&#xff0c;一个热衷于技术分享的小伙伴。最近在面试的时候遇到了一个非常有趣的问题&#xff0c;也是很多人关心的话题——字节跳动产品面试题&#xff1a;说一下抖音和快手的推荐策略有什么不同&#xff1f;今天&#xff0c;就让我们一起来揭开这…

前端vue uni-app使用Vue和ECharts构建交互式树形结构图

题目&#xff1a;使用Vue和ECharts构建交互式树形结构图 摘要&#xff1a;本文介绍了如何使用Vue.js和ECharts构建一个交互式的树形结构图。通过整合ECharts的强大可视化功能&#xff0c;我们创建了一个可拖拽移动、点击展开和收缩的树形结构图&#xff0c;并实现了无限添加子…

《Spring Cloud学习笔记:微服务保护Sentinel + JMeter快速入门》

Review 解决了服务拆分之后的服务治理问题&#xff1a;Nacos解决了服务治理问题OpenFeign解决了服务之间的远程调用问题网关与前端进行交互&#xff0c;基于网关的过滤器解决了登录校验的问题 流量控制&#xff1a;避免因为突发流量而导致的服务宕机。 隔离和降级&#xff1a…

Java EE Servlet之Servlet API详解

文章目录 1. HttpServlet1.1 核心方法 2. HttpServletRequest3. HttpServletResponse 接下来我们来学习 Servlet API 里面的详细情况 1. HttpServlet 写一个 Servlet 代码&#xff0c;都是要继承这个类&#xff0c;重写里面的方法 Servlet 这里的代码&#xff0c;只需要继承…

【数据结构】第2章线性表(头歌习题)【合集】

文章目录 第1关&#xff1a;实现顺序表各种基本运算的算法任务描述编程要求完整代码 第2关&#xff1a;实现单链表各种基本运算的算法任务描述编程要求完整代码 第3关&#xff1a;移除顺序表中所有值等于x的元素任务描述编程要求完整代码 第4关&#xff1a;逆置顺序表任务描述编…

穷举vs暴搜vs深搜vs回溯vs剪枝

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;那个传说中的man的主页 &#x1f3e0;个人专栏&#xff1a;题目解析 &#x1f30e;推荐文章&#xff1a;题目大解析&#xff08;3&#xff09; 目录 &#x1f449;&#x1f3fb;全排列&#x1f449;&#…

使用WSL

一、下载 Microsoft Store下载Ububtu&#xff0c;然后点击打开 二、报错WslRegisterDistribution failed with error: 0x800701bc 解决办法&#xff1a; 1、开启Windows Subsystem for Linux dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linu…

Unity之组件的生命周期

PS&#xff1a;第二天&#xff0c;依旧在摸鱼学unity 一、组件的概念 我本身是由Web后端转到了游戏后端&#xff0c;最近因为工作原因在学ET框架。学到了 ECS 编程模式开发&#xff08;E —— Entity&#xff0c;C —— Component &#xff0c; S —— System&#xff09;实体、…

玩转MYSQL|详细分析mysql-MGR集群搭建

目录 1、简介 2、环境准备 2.1 数据库服务器规划 2.2 安装mysql5.7.20 2.3 设置hostname和ip映射 3、创建复制环境 3.1 服务器host68.cn 3.1.1 配置/etc/my.cnf 3.1.2 服务器host68.cn上建立复制账号&#xff1a; 3.1.3 在mysql服务器host68.cn上安装group replicatio…

docker搭建minio集群,集群分享文件URL踩坑问题

一、环境准备 3台机器&#xff0c;Ip地址依次为IP1,IP2,IP3二、设置服务器时间同步 Minio集群需要各个节点的时间保持同步&#xff0c;使用NTP作为时间同步服务&#xff0c;这里以Minio-1&#xff08;IP1&#xff09;为上游服务器&#xff0c;其它2个节点为下游服务器&#x…

idea实现Java连接MySQL数据库

1.下载MySQL并安装 首先如果没有mysql的需要先下载MySQL&#xff0c;可以看这个教程&#xff1a; Mysql超详细安装配置教程(保姆级)_mysql安装及配置超详细教程-CSDN博客 2.下载mysql 的jdbc驱动 官网&#xff1a;MySQL :: Download Connector/J 解压并将驱动jar包导入id…

信息网络协议基础_IP移动网络管理

文章目录 概述移动IPv6待解决的问题关键词基本过程分组拦截技术移动检测和转交地址自动配置到家乡代理绑定注册通信对端不支持IPv6通信对端支持移动IPv6 对IP以上层屏蔽移动性 移动IPv6存在的问题移动IPv6优化代理移动IP概述原理基本过程初始接入切换 概述 移动IPv6 待解决的问…

证明:切线垂直于半径

证明&#xff1a; 切线垂直于过切点的半径。 下面是网上最简单的证明方法。 证明&#xff1a; 利用反证法。 如下图所示&#xff0c;直线AB和圆O切于点A&#xff0c;假设OA 不垂直于 AB&#xff0c;而 O B ⊥ A B OB \perp AB OB⊥AB&#xff0c;则 ∠ O B A 90 \angle OB…

Apache SSI 远程命令执行漏洞

一、环境搭建 二、访问upload.php 三、写shell <!--#exec cmd"id" --> 四、访问 如图所示&#xff0c;即getshell成功&#xff01;​

C#上位机与欧姆龙PLC的通信06---- HostLink协议(FINS版)

1、介绍 对于上位机开发来说&#xff0c;欧姆龙PLC支持的主要的协议有Hostlink协议&#xff0c;FinsTcp/Udp协议&#xff0c;EtherNetIP协议&#xff0c;本项目使用Hostlink协议。 Hostlink协议是欧姆龙PLC与上位机链接的公开协议。上位机通过发送Hostlink命令&#xff0c;可…

Spring Cloud Gateway集成Knife4j

1、前提 网关路由能够正常工作。 案例 基于 Spring Cloud Gateway Nacos 实现动态路由拓展的参考地址&#xff1a;Spring Cloud Gateway Nacos 实现动态路由 详细官网案例&#xff1a;https://doc.xiaominfo.com/docs/middleware-sources/spring-cloud-gateway/spring-gatewa…

使用anaconda创建爬虫spyder工程

1.由于每个工程使用的环境都可能不一样&#xff0c;因此一个好的习惯就是不同的工程都创建属于自己的环境&#xff0c;在anaconda中默认的环境是base&#xff0c;我们现在来创建一个名为spyder的环境&#xff0c;专门用于爬虫工程&#xff1a; //括号中名字&#xff0c;代表当…

【网络安全 | Misc】Aesop_secret(ISCC)

正文 动态gif&#xff0c;使用工具进行分解&#xff1a; https://tu.sioe.cn/gj/fenjie/ 得到ISCC字样 由Winhex看到密文&#xff1a; U2FsdGVkX19QwGkcgD0fTjZxgijRzQOGbCWALh4sRDec2w6xsY/ux53Vuj/AMZBDJ87qyZL5kAf1fmAH4Oe13Iu435bfRBuZgHpnRjTBn5xsDHONiR3t0Oa8yG/tOKJMN…

模式识别与机器学习-SVM(带软间隔的支持向量机)

SVM&#xff08;带软间隔的支持向量机&#xff09; 软间隔思想的由来软间隔的引入 谨以此博客作为复习期间的记录。 软间隔思想的由来 在上一篇博客中&#xff0c;回顾了线性可分的支持向量机,但在实际情况中&#xff0c;很少有完全线性可分的情况&#xff0c;大部分线性可分…

《末世少女/Zombie Girl》v1.0.0|容量13.6GB|官方简体介绍说明

《末世少女/Zombie Girl》v1.0.0|容量13.6GB|官方简体介绍说明 末世少女/Zombie Girl 一、游戏简介 《末世少女/Zombie Girl》是一款独特的第三人称射击游戏&#xff0c;以其惊心动魄的游戏体验、富有挑战性的丧尸战斗和深入的剧情探索而受到玩家们的热爱。这款游戏带领玩家进…