Java 多线程(七)—— 定时器

在这里插入图片描述

定时器介绍与使用

先简单介绍一下什么是定时器:定时器类似生活中的闹钟,当时间一到,我们就会去做某些事情。
在代码层面理解就是,当我们设置的时间一到,程序就会执行我们固定的代码片段(也就是任务)

在Java 标准库中给我们提供了定时器的类 Timer

下面是构造方法:
在这里插入图片描述

Timer() 就是直接创建一个定时器,里面的属性都是默认值
Timer(boolean isDaemon) 设置定时器里的线程是否为守护线程(后台线程)
Timer(String name) 给你的定时器设置一个名字


schedule

schedule 方法是 Timer 的核心方法,这里介绍第一个schedule 方法的参数含义。
在这里插入图片描述

首先 TimerTask task 是 要执行的任务,delay 是时间设置,单位是毫秒(ms)

意思就是在多少毫秒之后就会执行哪些任务。

TimerTask 其实就是对 Runnable 的进一步封装
在这里插入图片描述
因此我们在传递task 的时候,要重写 run 方法

代码演示:

public class Demo3 {public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello");}}, 5000);}
}

程序启动之后,5s 之后就会打印 hello

在这里插入图片描述

模拟实现

定时器需要定时执行一些任务,这些任务按照各自的时间执行,因此我们可以使用优先级队列来管理这些任务。
为了便于我们实现任务类(MyTimerTask)这里就不采用抽象类的写法,直接定义两个成员变量,一个是 Runnable 任务,一个是 delay 时间,并且在 MyTimerTask 这里实现 Comparable 接口,当然你也可以自己实现一个 Comparator

class MyTimerTask implements Comparable<MyTimerTask>{private Runnable task;private long delay;public MyTimerTask(Runnable task, long delay) {this.task = task;this.delay = delay;}@Overridepublic int compareTo(MyTimerTask o) {return (int)(this.delay - o.delay);}public Runnable getTask() {return task;}public long getDelay() {return delay;}
}

在模拟实现定时器类中,先定义一个优先级队列。

接着我们来写schedule 方法,首先schedule 需要传入两个参数,在Java 源码里是直接传入 task 对象,这里简单一点,直接传入 Runnable ,然后再传入一个 delay 延时时间。

在这个方法中实例化我们的 MyTimerTask 对象,注意时间我们不能直接传过去,我们需要处理一下时间,把 delay + System.currentTimeMillis() 作为时间传入,System.currentTimeMillis() 是系统此时的时间,加上 delay 就是这个任务要在哪个时刻开始执行。

为了避免因为多个线程同时传入任务而导致线程安全问题,这里我们进行加锁,为什么要使用 notify ,后面会提到。

    public synchronized void schedule(Runnable task, long delay) {MyTimerTask timerTask = new MyTimerTask(task, System.currentTimeMillis() + delay);queue.offer(timerTask);this.notify();}

最后就是在定时器里创建一个线程,这里我直接在定时器实例化的时候把线程创建出来。

线程的任务就是不断从队列里获取任务然后去执行,因此当队列没有元素的时候,我们需要进行线程等待状态,什么时候结束等待呢?就是当队列添加了一个元素之后,就发出 notify 信号结束等待,所以上面的 schedule 方法最后一行代码是 this.notify() 就是为了提醒这里要结束等待。

结束等待之后,我们就要获取堆顶元素,这里先不着急 poll(),因为我们还不确定这个任务是否是现在就立刻执行,所以我们先 peek(),接着进行判断,如果 task.getDelay() <= System.currentTimeMillis() 的时候就执行任务,否则就是 task.getDelay() > System.currentTimeMillis() 那就进入线程等待,此时我们不能采取死等策略,因为这时候可能没人会发出 notify() 来唤醒这个线程,除了 schedule 方法里 有 notify 之外,可是 schedule 方法的 notify 是起到因为 队列添加了元素,队列不为空唤醒上一个 wait 的作用,因此如果这时候没有人 schedule 的话,这个线程就永远都不会被唤醒了。

因此这里采取有时间的 wait ,this.wait(task.getDelay() - System.currentTimeMillis());,可是我们知道这个 wait 是有可能因为 schedule 唤醒的,这时候后面的代码不能直接写 任务执行,万一此时队列添加了一个更早的元素的话,按道理你执行的任务就应该更新,**并且此时时间一定没到,wait 提前被唤醒,也是不能执行任务的,**所以这里我们采取 if - else 代码块来写这里的代码,当线程确实被唤醒之后,没事,再走一遍上面的流程重新获取堆顶元素即可。

    public MyTimer() {Thread t = new Thread(() -> {try {while(true) {synchronized (this) {while (queue.isEmpty()) {this.wait();}MyTimerTask task = queue.peek();if(task.getDelay() > System.currentTimeMillis()) {this.wait(task.getDelay() - System.currentTimeMillis());} else {task.getTask().run();queue.poll();}}}} catch (InterruptedException e) {throw new RuntimeException(e);}});t.start();}

最终定时器模拟实现的代码:

class MyTimerTask implements Comparable<MyTimerTask>{private Runnable task;private long delay;public MyTimerTask(Runnable task, long delay) {this.task = task;this.delay = delay;}@Overridepublic int compareTo(MyTimerTask o) {return (int)(this.delay - o.delay);}public Runnable getTask() {return task;}public long getDelay() {return delay;}
}class MyTimer {private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();public MyTimer() {Thread t = new Thread(() -> {try {while(true) {synchronized (this) {while (queue.isEmpty()) {this.wait();}MyTimerTask task = queue.peek();if(task.getDelay() > System.currentTimeMillis()) {this.wait(task.getDelay() - System.currentTimeMillis());} else {task.getTask().run();queue.poll();}}}} catch (InterruptedException e) {throw new RuntimeException(e);}});t.start();}public synchronized void schedule(Runnable task, long delay) {MyTimerTask timerTask = new MyTimerTask(task, System.currentTimeMillis() + delay);queue.offer(timerTask);this.notify();}
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

谷歌新安装包文件形式 .aab 在UE4中的打包原理

摘要 本文学习了aab的基本概念以及UE4中产生aab的构建原理。 从官网了解基本概念 官网&#xff1a;Android Developers 1、什么是aab&#xff1f; .aab包形如&#xff1a; 2021年7月&#xff0c;在Google Play应用程序中&#xff0c;已经有数千个应用程序率先跟进了AAB格式。…

OpenCV视觉分析之运动分析(2)背景减除类:BackgroundSubtractorKNN的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 K-最近邻&#xff08;K-nearest neighbours, KNN&#xff09;基于的背景/前景分割算法。 该类实现了如 319中所述的 K-最近邻背景减除。如果前景…

Zypher Network Layer3 主网上线,“宝藏方舟”活动是亮点

前言 随着 Zytron Layer3 主网的上线&#xff0c;Zypher Network联合Linea共同推出了“宝藏方舟”活动&#xff0c;用户可通过参与活动&#xff0c;获得包括代币、积分、SBT等系列奖励。 Zypher Network 是一个以ZK方案为核心的游戏底层堆栈&#xff0c;其提供了一个具备主权…

C++20中头文件span的使用

<span>是C20中新增加的头文件&#xff0c;此头文件是containers库的一部分。包括&#xff1a; 1.模板类std::span&#xff1a;连续对象序列的非拥有视图(view)。std::span可以具有static extent&#xff0c;在这种情况下&#xff0c;序列中的元素数量在编译时已知并以typ…

探寻医疗行业人力资源管理系统优选方案

医疗机构管理日益重要&#xff0c;ZohoPeople HRMS助力医疗行业优化人力管理&#xff0c;提升效率。其涵盖智能排班、培训发展、合规保障、绩效管理等功能&#xff0c;支持全球化及远程协作&#xff0c;是医疗行业人力资源管理的有效工具。 一、医疗行业人力资源管理的复杂性 …

基于SpringBoot+Vue+uniapp微信小程序的社区门诊管理系统的详细设计和实现(源码+lw+部署文档+讲解等)

项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不是配置文件。Spring Boot 通过自动化配置和约…

15.正则化——防止过拟合的有效手段

引言 在人工智能(AI)领域&#xff0c;尤其是在机器学习和深度学习中&#xff0c;正则化(regularization)具有非常重要的地位。它不仅是训练模型过程中不可或缺的一部分&#xff0c;也是提高模型性能的关键因素之一。此外&#xff0c;正则化还可以提升模型的泛化能力&#xff0…

产品如何实现3D展示?具体步骤如下

产品实现3D展示主要依赖于先进的3D建模与展示技术。以下是产品实现3D展示的具体步骤和方法&#xff1a; 一、3D建模 使用专业的3D建模软件&#xff0c;如Blender、Maya、3ds Max等&#xff0c;这些软件提供了丰富的建模工具和材质编辑器&#xff0c;能够创建出高精度的3D模型…

Flutter 12 实现双击屏幕显示点赞爱心多种动画(AnimationIcon)效果

本文主要是使用Flutter封装一个双击屏幕显示点赞爱心UI效果&#xff0c;并实现了爱心Icon 透明度、缩放、旋转、渐变等动画效果。 实现效果&#xff1a; 实现逻辑&#xff1a; 1、封装FavoriteGesture&#xff08;爱心手势&#xff09;实现双击屏幕显示爱心Icon&#xff1b; …

文件摆渡系统选型指南:如何找到最适合您的数据安全解决方案?

在当今数字化时代&#xff0c;数据的安全传输与共享已成为企业运营中不可或缺的一环。文件摆渡系统&#xff0c;作为实现数据在不同安全域之间高效、安全传输的重要工具&#xff0c;其选型直接关系到企业数据的安全性与业务效率。本文将为您详细介绍如何挑选最适合您企业的文件…

视频网站系统的设计与实现(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 题目&#xff1a;视频网站系统 摘 要 使用旧方法对视频信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运用在视频信息的管理上面可以解决许多信息管理上面的难题&#xff0c;比如处理数据时间很长&#…

为什么k8s不支持docker-kubernetes

为什么Kubernetes不再支持Docker&#xff1f; 在Kubernetes 1.20版本之后&#xff0c;Kubernetes宣布逐步停止对Docker作为容器运行时的支持。这一改变在容器管理领域引起了广泛关注。许多人不禁疑惑&#xff1a;Kubernetes与Docker一向密切合作&#xff0c;为何会做出这样的决…

骨传导耳机哪个品牌好?2024年五大热门精选骨传导耳机推荐

在当今快节奏的生活中&#xff0c;人们对于个人音频设备的需求不仅限于优质的音质体验&#xff0c;还越来越注重健康与舒适。骨传导耳机作为一种新兴的技术产品&#xff0c;以其独特的听觉传递方式——通过颞骨而非耳道传递声音——赢得了众多用户的青睐。这种技术不仅可以提供…

Webserver(1)Linux开发环境搭建

目录 配置软件虚拟机中安装ubuntu安装ubuntu18的操作系统 安装VM tools安装XshellVscode远程连接到虚拟机 配置软件 VMwareVScodeg安装ubuntu 18.04.iso 或者镜像版本 XShellXFTP 虚拟机中安装ubuntu 安装ubuntu18的操作系统 开启虚拟机 选择中文简体 安装VM tools 打开v…

炸了!改进Transformer!Transformer-BiGRU多变量回归预测(Matlab)

炸了&#xff01;改进Transformer&#xff01;Transformer-BiGRU多变量回归预测&#xff08;Matlab&#xff09; 目录 炸了&#xff01;改进Transformer&#xff01;Transformer-BiGRU多变量回归预测&#xff08;Matlab&#xff09;分类效果基本介绍程序设计参考资料 分类效果 …

python离线安装依赖

以pymsql依赖为例操作如下: Python Package Index&#xff08;PyPI&#xff09;的官方网址是&#xff1a; PyPI The Python Package Index 在这个网站上&#xff0c;你可以搜索、浏览和下载Python包。 tar -xvzf pymysql2-1.3.3.tar.gz cd pymysql2-1.3.3 python setup.p…

基于SSM机场网上订票系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;机票信息管理&#xff0c;订单信息管理&#xff0c;机场广告管理&#xff0c;系统管理 前台账号功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;机票信息&#xf…

CRMEB标准版Mysql修改sql_mode

数据库配置 1.宝塔控制面板-软件商店-MySql-设置 2.点击配置修改&#xff0c;查找sql-mode或sql_mode &#xff08;可使用CtrlF快捷查找&#xff09; 3.复制 NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 然后替换粘贴&#xff0c;保存 注&#xff1a;MySQL8.0版本的 第三步用…

CRM在企业协同中发挥了哪些作用?

在当今快速变化的商业环境中&#xff0c;企业竞争力的核心已逐渐从单一的产品或服务优势&#xff0c;转向以客户为中心的综合能力构建。客户关系管理&#xff08;CRM&#xff09;系统作为这一转型的关键驱动力&#xff0c;正以前所未有的力度打破企业内部部门间的传统壁垒&…

MusePose模型部署指南

一、模型介绍 MusePose是一个基于扩散和姿势引导的虚拟人视频生成框架。 主要贡献可以概括如下&#xff1a; 发布的模型能够根据给定的姿势序列&#xff0c;生成参考图中人物的舞蹈视频&#xff0c;生成的结果质量超越了同一主题中几乎所有当前开源的模型。发布该 pose alig…