定时器详解

定时器:Timer类

常用方法方法:

1.schedule(TimeTask timetask,long delay,(long period)):

TimeTask:实现了Runnable类,实现时需要重写run方法

delay:表示延迟多少(decay)后开始执行任务,单位是毫秒,这个参数也可以是日期(Date)

period:周期时间,表示定时器循环执行任务之间的间隔时间,时间是毫秒

如果没有period参数,那么就只执行一次任务内容

2.scheduleAtFixedRate(TimeTask timetask,long delay,long period):

参数和schedule的参数相同,其作用为定时器设置循环执行的内容,第一次执行内容的延迟时间,循环的周期时间。可以看出schedule方法的功能其实已经包括这个方法了

3.cancel():关闭计时器


schedule(TimeTask timetask,long delay)

public static void main(String[] args) {System.out.println("任务三秒后开启");Timer t = new Timer();//定时执行任务 表示几秒后执行run方法里面的内容t.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("任务开启");}},3000);}


schedule(TimeTask timetask,long delay,long period):

 public static void main(String[] args) {System.out.println("任务三秒后开启");Timer t = new Timer();//定时执行任务 表示几秒后执行run方法里面的内容t.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("开始以1秒的时间间隔循环执行任务");}},2000,1000);}


scheduleAtFixedRate(TimeTask timetask,long delay,long period):

 public static void main(String[] args) {System.out.println("任务两秒后开启");Timer t = new Timer();//定时执行任务 表示几秒后执行run方法里面的内容t.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("任务开启");}},2000);t.scheduleAtFixedRate(new TimerTask() {@Overridepublic void run() {System.out.println("开始循环任务(间隔1s)");}},2000,1000);}


指定定时器执行固定个任务后结束
public static void main(String[] args) {Timer t = new Timer();int timeCount = 5;long delay = 1000;long period = 1000;t.schedule(new TimerTask() {int count = 1;@Overridepublic void run() {if(count >= timeCount){t.cancel();}System.out.println("执行任务:"+count);count++;}},delay,period);}


模拟实现定时器

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;class Task implements Comparable<Task>{//这个类用来描述任务的private Runnable runnable;//执行的任务时间private long time; //time + System.currentTimeMillis()public Task(Runnable runnable,long time){this.runnable = runnable;this.time = time;}public long getTime(){return time ;}public void run() {runnable.run();}//比较规则 执行时间在前的先执行@Overridepublic int compareTo(Task o) {return (int)(this.time - o.time);}
}
public class MyTimer{//一个阻塞队列private BlockingQueue<Task> queue = new PriorityBlockingQueue<>();//扫描线程private Thread t = null;public MyTimer(){t = new Thread(() -> {while(true) {try {Task task = queue.take();if (task.getTime() > System.currentTimeMillis()) {//还没到时间queue.put(task);} else {//执行任务task.run();}} catch (InterruptedException e) {e.printStackTrace();}}});t.start();}public void schedule(Runnable runnable,long time) throws InterruptedException {//在这一步加System.currentTimeMillis() 不能在getTime方法中加,不然就会一直大于System.currentTimeMillis()Task task = new Task(runnable, time+System.currentTimeMillis());queue.put(task);}public static void main(String[] args) throws InterruptedException {MyTimer m = new MyTimer();m.schedule(new Runnable() {@Overridepublic void run() {System.out.println("这是任务1");}},2000);m.schedule(new Runnable() {@Overridepublic void run() {System.out.println("这是任务2");}},1000);}
}


问题:
1.忙等

假设此时时间是上午9点,第一个任务的时间是上午10点,那么在这一个小时内,程序会一直重复执行一个代码:

 Task task = queue.take();if (task.getTime() > System.currentTimeMillis()) {//还没到时间queue.put(task);}

这行代码可能会循环个非常多亿次,且别忘了,优先级队列的底层是用堆实现的,每当我们取出一个元素又出现插入时,根据堆的调整,此元素又会在堆顶,而每一层调整都是有开销的,故这一时间段的开销是重复且多余的,所以我们就等一次,等堆顶任务执行时间与当前时间的差值就可,修改代码:

 Task task = queue.take();if (task.getTime() > System.currentTimeMillis()){//还没到时间queue.put(task);Thread.sleep(task.getTime() - System.currentTimeMillis());

大伙们是不是觉得此时的代码就已经完美了?!!

然而并不是! 我们并不能使用sleep休眠来休眠两时间差

试想一下,如果咱们休眠的时候突然插进来了一个上午9.30执行的任务,那么这个任务就不会被执行到!故使用带参版本的wait()方法才是最好的选择

再次修改代码:

 if (task.getTime() > System.currentTimeMillis()){//还没到时间queue.put(task);synchronized (this){this.wait(task.getTime() - System.currentTimeMillis());}public void schedule(Runnable runnable,long time) throws InterruptedException {Task task = new Task(runnable, time+System.currentTimeMillis());queue.put(task);//唤醒t线程里的wait操作this.notify();}

这个时候代码看起来是不是似乎万无一失了已经!

but,我们来考虑一个极端极端极端极端的情况


2.极端情况
 if (task.getTime() > System.currentTimeMillis()){//还没到时间queue.put(task);synchronized (this){this.wait(task.getTime() - System.currentTimeMillis());}

如果线程1在执行到queue.put(task)时,恰好被调度走了,此时另一个线程调用schedule方法,且如果当线程2的任务时间小于线程1的任务时间时,此时线程2的任务就不会执行到。因为线程2的notify没作用,线程1都还没有执行到wait方法,但线程1重新执行时,此时的时间差已经是固定了的,但是这个时间差要大于线程2任务执行的时间,故线程2就会被“极端”地错过


完美代码

加大锁的力度:造成此极端情况的原因即为:take和wait是多步操作,非原子性

public MyTimer(){t = new Thread(() -> {while(true) {synchronized (this){try {Task task = queue.take();if (task.getTime() > System.currentTimeMillis()) {//还没到时间queue.put(task);this.wait(task.getTime() - System.currentTimeMillis());} else {//执行任务task.run();}} catch (InterruptedException e) {e.printStackTrace();}}}});t.start();}

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

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

相关文章

java算法day2

螺旋矩阵搜索插入位置查找元素第一个位置和最后一个位置 螺旋矩阵 解法&#xff1a;模拟&#xff0c;核心在于你怎么转&#xff0c;还有就是处理边界&#xff0c;边界如何收缩&#xff0c;什么时候停止旋转。最内圈的时候怎么处理。 通过上图的模拟来解决这个问题&#xff1a;…

SpringBoot-自定义注解AOP实现及拦截器示例

SpringBoot-自定义注解AOP实现及拦截器示例 一、四大元注解 当你在编写自定义注解时&#xff0c;Target、Retention、Documented 和 Inherited 是四个你可能会用到的元注解&#xff0c;它们可以帮助你更好地定义和使用注解。 1、Target Target 注解用于指定注解可以应用的程…

【科研入门】评价指标AUC原理及实践

评价指标AUC原理及实践 目录 评价指标AUC原理及实践一、二分类评估指标1.1 混淆矩阵1.2 准确率 Accuracy定义公式局限性 1.3 精确率 Precision 和 召回率 Recall定义公式 1.4 阈值定义阈值的调整 1.5 ROC与AUC引入定义公式理解AUC算法 一、二分类评估指标 1.1 混淆矩阵 对于二…

【muzzik 分享】关于 MKFramework 的设计想法

MKFramework是我个人维护持续了几年的项目&#xff08;虽然公开只有一年左右&#xff09;&#xff0c;最开始由于自己从事QP类游戏开发&#xff0c;我很喜欢MVVM&#xff0c;于是想把他做成 MVVM 框架&#xff0c;在论坛第一个 MVVM 框架出来的时候&#xff0c;我的框架已经快完…

函数调用栈中的栈帧形成了一个链式结构

下面是一个简单的 C 示例&#xff0c;演示了函数调用栈的概念&#xff1a; #include <iostream>// 递归函数&#xff0c;计算阶乘 int factorial(int n) {if (n 0 || n 1) {return 1;} else {return n * factorial(n - 1); // 递归调用} }int main() {int result fac…

电机控制专题(二)——Sensorless之扩展反电动势EEMF

文章目录 电机控制专题(二)——Sensorless之扩展反电动势EEMF前言理论推导仿真验证总结参考文献 电机控制专题(二)——Sensorless之扩展反电动势EEMF 前言 总结下电机控制中的扩展反电动势模型。 纯小白&#xff0c;如有不当&#xff0c;轻喷&#xff0c;还请指出。 在得出E…

代码随想录算法训练营Day17 | 110.平衡二叉树、257. 二叉树的所有路径、404.左叶子之和 | Python | 个人记录向

本文目录 110.平衡二叉树做题看文章 257. 二叉树的所有路径做题看文章 404.左叶子之和做题看文章 以往忽略的知识点小结个人体会 110.平衡二叉树 代码随想录&#xff1a;110.平衡二叉树 Leetcode&#xff1a;110.平衡二叉树 做题 今天算是第一次用递归做出来了&#xff0c;之…

《神经网络与深度学习:案例与实践》动手练习1.3

飞桨AI Studio星河社区-人工智能学习与实训社区 动手练习1.3 执行上述算子的反向过程&#xff0c;并验证梯度是否正确。 import mathclass Op(object):def __init__(self):passdef __call__(self, inputs):return self.forward(inputs)# 前向函数# 输入&#xff1a;张量inpu…

synchronized锁升级原理

锁升级过程 jdk1.6之后的优化 synchronized锁有四种状态&#xff0c;无锁&#xff0c;偏向锁&#xff0c;轻量级锁&#xff0c;重量级锁&#xff0c;这几个状态会随着竞争状态逐渐升级&#xff0c;锁可以升级但不能降级&#xff0c;但是偏向锁状态可以被重置为无锁状态。 1、偏…

深入挖掘C语言 ---- 文件操作

目录 1. 文件的打开和关闭1.1 流和标准流1.1.1流1.1.2标准流 1.2 文件指针1.3 文件的打开和关闭 2. 顺序读写3. 随机读写3.1 fseek3.2 ftell3.3 rewind 4. 读取结束判定 正文开始 1. 文件的打开和关闭 1.1 流和标准流 1.1.1流 我们程序的数据需要输出到各种外部设备, 也需要…

CentOS7升级openssl

文章目录 一 系统环境二 操作步骤三 版本检查 一 系统环境 公司服务器等保要求&#xff0c;修复openssl的高危漏洞。 本机使用centos7.9系统&#xff0c;openssl版本是1.0.2k&#xff0c;计划升级到1.1.1q 在执行下列操作前&#xff0c;务必要打快照做好备份&#xff0c;以防升…

docker 容器中安装cron,却无法启动定时任务

问题描述&#xff1a; 当我是在Dockerfile配置安装cron RUN apt-get update && apt-get install -y cron 或者进入容器中安装cron apt-get install -y cron 都会有个问题就是cron服务正常启动&#xff0c;但是加入到/etc/con.d下的任务&#xff0c;或者crontab -…

基于STM32单片机的智能家居环境监测与控制系统设计

基于STM32单片机的智能家居环境监测与控制系统设计 摘要&#xff1a;随着物联网技术的不断发展&#xff0c;智能家居环境监测与控制系统的应用越来越广泛。本文设计了一种基于STM32单片机的智能家居环境监测与控制系统&#xff0c;该系统能够实时监测环境中的温湿度和天然气浓…

浮点数随机生成器

浅做了一个数值模拟器&#xff0c;支持自定义多路数据模拟。数据源支持浮点型、整形等多种类型&#xff0c;通讯支持网口和串口&#xff0c;支持指定协议。简略效果大概如下&#xff0c;后续可能会考虑开源~ [code] 浮点数生成器 #include <iostream> #include <ra…

Spring的事务传播机制有哪些

Spring的事务传播机制有哪些&#xff1f; Spring的事务传播机制用于控制在多个事务方法相互调用时事务的行为。 在复杂的业务场景中&#xff0c;多个事务方法之间的调用可能会导致事务的不一致&#xff0c;如出现数据丢失、重复提交等问题&#xff0c;使用事务传播机制可以避…

Scala 04 —— 函数式编程底层逻辑

函数式编程 底层逻辑 该文章来自2023/1/14的清华大学交叉信息学院助理教授——袁洋演讲。 文章目录 函数式编程 底层逻辑函数式编程假如...副作用是必须的&#xff1f;函数的定义函数是数据的函数&#xff0c;不是数字的函数如何把业务逻辑做成纯函数式&#xff1f;函数式编程…

【python】直接在python3下安装 jupyter notebook,以及处理安装报错,启动不了问题

目录 问题&#xff1a; 1 先做准备&#xff0c;查看环境 1.1 先看python3 和pip &#xff0c;以及查看是否有 juypter 1.2 开始安装 1.3 安装完成后得到警告和报错 2 处理安装的报错问题 2.1 网上有说是因为 pip 自身需要更新&#xff0c;更新之 2.1.1 更新pip 2.1.…

从写博客到现在的感受

从写博客开始到现在我已经写了35篇博客了&#xff0c;慢慢的了解发现&#xff0c;越是深入了解代码&#xff0c;我就感觉到自己的渺小与不足&#xff0c;感觉要写的东西实在是太多了&#xff0c;我发现&#xff1a;以前我是个初学小白&#xff0c;现在依然是个初学小白&#xf…

c++复习笔记

前言 为什么写C复习笔记&#xff1f;脑子不好使&#xff0c;今天学明天忘。 为什么一堆代码&#xff1f;代码是敲出来的&#xff0c;不是看出来的。里面的代码都运行过&#xff0c;萌新跟着敲就完事了&#xff0c;也有注释辅助理解。至于有基础的&#xff0c;代码就这么点&am…

互联网十万个为什么之什么是产品经理?

什么是产品经理&#xff1f; 你知道每当你打开手机&#xff0c;点开一个应用程序&#xff0c;或者在网上购物时&#xff0c;那背后的幕后英雄是谁吗&#xff1f;这就是产品经理。他们是那些负责设计、开发和推广产品的人&#xff0c;他们的工作是确保你的体验顺畅而愉快。 产…