【Java多线程从青铜到王者】定时器的原理和实现(十一)

定时器

定时器时我们日常开发中会用到的组件工具,类似于一个"闹钟",设定一个时间,等到了时间,定时器最自动的去执行某个逻辑,比如博客的定时发布,就是使用到了定时器

Java标准库里面也提供了定时器的实现
在这里插入图片描述
在这里插入图片描述
定义一个timer,添加多个任务,每个任务都带有一个时间

定义任务的时候可以使用lambde表达式吗?

  • 答案是不能的,lambde只能用于创建函数式接口的实例,如果非要用lambde表达式创建一个类的实例的话,可以用lambde先创建出一个函数式接口的实例,再把这个函数式接口赋值给类的变量,如下图,我们的源代码里面Timer就是一个实现了Runnable函数式接口的一个抽象类,是不可以使用lambde表达式的
    在这里插入图片描述

内置了前台线程

在这里插入图片描述
我们上图代码的执行结果如下
在这里插入图片描述
我们发现,控制台打印了我们的三个任务以后,并没有进程结束的提示,说明我们的进程并没有结束,原因是Timer里面内置了前台线程,它会阻止进程的结束

但是我们往timer里面添加的任务都执行完了,也不会结束吗?

  • 因为我们的timer也不知道你是否还会添加新任务进来,所以它不能结束,必须严阵以待,也就是不能结束,于是内置了前台线程

但是就没有办法让timer结束吗?

  • timer里面有一个cancel方法,可以手动调用来结束进程
    在这里插入图片描述
    上述代码我们调用了cancel后,进程会结束(如下图)
    在这里插入图片描述
    需要主动调用cancel让线程主动结束,要不然Timer不知道是否还有其他地方要添加任务的

定时器的实现

我们先思考一下,实现一个定时器需要哪些的内容

  • 首先我们需要一个线程,帮助我们掐算时间,时间一到的话,这个线程就会执行该任务
  • 其次我们需要一个容器,能够保存schedule进来的任务

我们直观的来想的话,我们这个线程就要不断的遍历我们这个容器,看看任务的时间是否到了,如果到了,就执行这个任务
但是如果我们容器里面的元素很多呢?要是遍历的话,时间复杂度就是o(n)了,开销就很大了
我们此时就需要用到优先级队列了,我们的每个任务都有实现,先执行时间小的,后执行时间大的,有了优先级队列,必须是小根堆,队首元素就是执行时间最小的元素,我们每次只需要看一下队首元素是否到时间了,要是队首元素也没有到时间的话,其他的任务一定没有到时间

我们现在的优先级队列有两个选择:

  1. PriorityQueue(线程不安全)
  2. PriorityBloskingQueue(线程安全)

虽然PriorityBloskingQueue是线程安全的,但是我们这里要使用 PriorityQueue,使用PriorityBloskingQueue不太好控制,容易出问题,我们这里手动给PriorityQueue加锁即可

在这里插入图片描述
上面是我们写的一个类,用来描述一个任务

我们这个类直接实现Runnable也是可以的,我们这里是让这个类持有了Runnable

而且我们这个任务类是需要将其放到优先级队列里面的,所以要求我们这个类是可以比较的,我们这里也实现了Comparable接口,并且重写了CompareTo方法,这里的比较的规则就是时间的大小

如果是TreeSet和TreeMap的话我们要求元素是可以比较的,我们就需要实现Comparable和Comparator接口
如果是HsahSet和HashMap的话,就要求元素是可以比较相等的和可哈希的,这时候就要equals和hashCode方法了,有时候为了让hsah更加高效的话,需要重写这两个方法

在这里插入图片描述
这是我们写的构造方法,在这里面我们创建了一个线程,这个线程就是我们的判断和执行任务的线程

加锁

由于我们的加入任务的操作(如下图)和我们的执行并且删除任务的操作都是对同一个队列进行操作,可能会有线程安全问题
在这里插入图片描述
可以发现我们都给这两个方法加上锁了
当我们线程如果发现队列里面是空的,locker.wait就会释放锁,于是scedule就可以获取到锁了,获取到锁之后就可以往队列里面添加任务,添加完任务之后,notify就可以将线程唤醒

在这里插入图片描述
我们红框部分的地方也要用到wait,是有时间限制的wait,这里有两种情况:

  1. 我们能进入else就说明还没有到首队列元素的执行时间,此时我们需要等待(任务执行时间-当前时间)这么多的时间,等到了我们wait的时间,wait就会自动唤醒,再次进入循环取出队首元素,发现到时间了,于是就执行任务,并且将任务从队列里面删除
  2. 我们进入else,执行wait释放锁并且阻塞的时候,此时又有一个任务通过schedule进入到了队列,schedule里面的notify就会将我们的线程唤醒,线程唤醒之后又进入循环,由于我们加入了新的任务,这个新任务的时间有可能比我们原来的队首任务的时间小,也有可能大,这时我们的队首任务就会发生变化,于是又peek一下,取队首元素,看看是否到了时间,在根据条件执行下面的逻辑

如果我们else里面什么都不加的话,只有一个coontinue,这时我的CPU只是在忙等,虽然在等,但是CPU很忙,因为此时线程的情况是进入了死循环,不如将CPU的资源让出来,给其他的线程使用,这就是我们wait的另一个作用

使用sleep可以吗?
不可以,sleep虽然也可以实现等待的结果,但是sleep没有释放锁,如果我们中间有一个schedule又加入了一个任务,这个任务的执行时间是10:20:00,比如现在是10:00:00,我们的首队列元素的执行时间是10:30:00,此时我们进入else执行到了sleep,按照我们上面wait的代码,我们这里要sleep30分钟,这个过程中,锁没有被释放,是被判断的线程持有的,这就导致我们的schedule方法获取不到锁,无法将新的任务添加到队列,等sleep到了时间被唤醒的时候,已经过了10:20:00了,这个任务就错过了

执行过程详解

在这里插入图片描述
执行到这里以后我们会进入到漫长的3s的等待,3s结束之后,wait就自动唤醒了
在这里插入图片描述

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

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

相关文章

深入剖析AI大模型:Prompt 优化的底层逻辑

记得看到一篇NLP的文章,就 Prompt 时序效应的论文揭示了一个有趣现象,文章中说:模型对指令的解析存在 "注意力衰减" 特性 —— 就像人类阅读时会更关注段落开头,模型对 Prompt 前 20% 的内容赋予的权重高达 60%。这个发…

【备忘】PHP web项目一般部署办法

【PHP项目一般部署办法】 操作步骤 代码: 把php项目代码clone到指定位置如www/下新建php站点,填写域名,把站点根目录设置为项目根目录项目入口设置,一般为public/项目权限改为766(特殊时候可设置为777),如果有特殊要求…

【60 Pandas+Pyecharts | 箱包订单数据分析可视化】

文章目录 🏳️‍🌈 1. 导入模块🏳️‍🌈 2. Pandas数据处理2.1 读取数据2.2 数据信息2.3 去除订单金额为空的数据2.5 提取季度和星期 🏳️‍🌈 3. Pyecharts数据可视化3.1 每月订单量和订单金额分布3.2 各季…

玩转Docker | 使用Docker部署vaultwarden密码管理器

玩转Docker | 使用Docker部署vaultwarden密码管理器 前言一、vaultwarden介绍Vaultwarden 简介主要特点二、系统要求环境要求环境检查Docker版本检查检查操作系统版本三、部署vaultwarden服务下载vaultwarden镜像编辑部署文件创建容器检查容器状态检查服务端口安全设置四、配置…

晶振的多面舞台:从日常电子到高精尖科技的应用探秘

在现代科技的宏大舞台上,晶振宛如一位低调却至关重要的幕后主角,以其稳定的频率输出,为各类电子设备赋予了精准的“脉搏”。从我们日常生活中须臾不离的电子设备,到引领时代前沿的高精尖科技领域,晶振都发挥着不可替代…

uni-app 小程序 Cannot read property ‘addEventListener‘ of undefined, mounted hook

在用 uni-app 开发微信小程序时,提示 Cannot read property addEventListener of undefined, mounted hook document.addEventListener("mousemove", this.touchmove) 在小程序开发里,addEventListener 并非通用的标准 API,不过与…

《专业小词开课啦》——幂等

在系统对接过程中,当出现接口调用异常的情况时,程序员可能会用一些专业术语来答疑......对于0基础同学,自然是需要自行百度一番,学习一下! 接下来,先学习【幂等】 PS: 小白参考1.1~1.4内容即…

渗透实战PortSwigger Labs指南:自定义标签XSS和SVG XSS利用

阻止除自定义标签之外的所有标签 先输入一些标签测试&#xff0c;说是全部标签都被禁了 除了自定义的 自定义<my-tag onmouseoveralert(xss)> <my-tag idx onfocusalert(document.cookie) tabindex1> onfocus 当元素获得焦点时&#xff08;如通过点击或键盘导航&…

利用pycharm搭建模型步骤

1 如何将别人论文的代码跑起来&#xff0c;以Pycharm为例&#xff0c;在下载代码的时候&#xff0c;要注意使用的python版本是多少&#xff0c;并且要注意使用的keras和tensorflow等文件夹的版本&#xff0c;我们可以直接使用pycharm中file文件中的settings&#xff0c;来添加相…

Qt 中directoryChanged监听某个目录的内容是否发生变化

Qt 中&#xff0c;directoryChanged 是 QFileSystemWatcher 类的一个信号&#xff0c;用于监听某个目录的内容是否发生变化&#xff08;如添加、删除文件或子目录&#xff09; ✅ 一、功能说明 QFileSystemWatcher::directoryChanged(const QString &path) 信号的作用是&…

JavaWeb(Servlet预习)

案例1&#xff1a;基于jspServlet实现用户登录验证 1.input.jsp <% page language"java" contentType"text/html; charsetUTF-8"pageEncoding"UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset"UTF-8&q…

Docker Compose 部署 Prometheus + Grafana

安装 docker-compose.yml version: 3.8services:# Prometheus 监控服务prometheus:image: prom/prometheus:latestcontainer_name: prometheusrestart: unless-stoppedvolumes:- ./conf/prometheus.yml:/etc/prometheus/prometheus.yml- ./prometheus_data:/prometheuscomman…

准确--使用 ThinBackup 插件执行备份和恢复

使用 ThinBackup 插件执行备份和恢复 导出&#xff08;备份&#xff09;步骤&#xff1a; 进入 Manage Jenkins > ThinBackup。设置 Backup schedule for full backups&#xff08;可选&#xff09;&#xff0c;并配置 Files to exclude&#xff08;可选&#xff09;。点击…

Qt Creator 从入门到项目实战

Qt Creator 简介 Qt Creator 是一个跨平台的集成开发环境&#xff08;IDE&#xff09;&#xff0c;专门用于开发 Qt 应用程序。它为开发者提供了一个强大的工具集&#xff0c;包括代码编辑器、调试器、UI 设计器以及性能分析工具等。 1.1 Qt Creator 的安装 Qt Creator 支持…

公司内网远程访问配置教程:本地服务器(和指定端口应用)实现外网连接使用

在数字化时代&#xff0c;企业的办公模式日益多元化&#xff0c;远程办公、跨地区协作等需求不断增加。这使得在公司内网中配置远程访问变得至关重要&#xff0c;它能让员工无论身处何地&#xff0c;只要有网络连接&#xff0c;就能便捷地访问公司内部的各类资源&#xff0c;如…

边缘计算如何重塑能源管理?从技术原理到应用场景全解析

在全球能源数字化转型的浪潮中&#xff0c;一个看似不起眼的设备正在悄悄改变工业能效管理的模式 —— 这就是边缘计算网关。以能源领域为例&#xff0c;传统的 "设备 - 云端" 二层架构正面临数据传输延迟、网络带宽压力大、断网失效等挑战&#xff0c;而边缘计算技术…

自主导航巡检机器人系统解决方案

自主导航巡检机器人系统解决方案 运动性能强大的通用型履带式机器人底盘&#xff0c;整车采用克里斯蒂全独立悬挂设计&#xff0c;内部搭载高扭矩无刷电机&#xff0c;通过精心匹配的底盘高度和功率配置&#xff0c;底盘表现出卓越的通过性能、低重心、平稳运行以及高效的传动效…

Vim 撤销 / 重做 / 操作历史命令汇总

Vim 撤销 / 重做 / 操作历史命令汇总 Vim 提供了丰富的撤销&#xff08;undo&#xff09;、重做&#xff08;redo&#xff09;及查看操作历史的命令&#xff0c;帮助你在编辑过程中灵活地回退或前进到任意修改点。下面按功能分类整理常用命令&#xff0c;便于快速查阅和记忆。…

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…

项目四.高可用集群_ansible

设备准备 安装wordpress [rootlocalhost ~]# nmcli c del "Wired connection 1" [rootlocalhost ~]# nmcli c add type ethernet ifname ens224 con-name ens224 ipv4.method manual ipv4.addr 192.168.88.40/24 gw4 192.168.88.1 autoconnect true [rootlocalhos…