定时器详解

定时器: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;我的框架已经快完…

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

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

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;以防升…

浮点数随机生成器

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

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.…

Redis搭建主从

Redis搭建主从: 1:拉取Redis镜像 docker pull redis2:创建主从对应的目录结构 3:对redis6379.log,redis6380.log,redis6381.log进行授权 chmod 777 redis6379.log chmod 777 redis6380.log chmod 777 redis6381.log4:修改主(master)的配置文件 5:创建主(master) redis_6379 …

docker部署java项目,如何docker-compose内的jdk版本与本地版本保持一致

目录结构 /var └── data├── docker-compose.yml └── docker├── Dockerfile└── jdk-8u401-linux-x64.tar.gzdockerfile文件 FROM ubuntu:latest# 拷贝本地服务器上的 JDK 安装包到 Docker 镜像中 COPY jdk-8u401-linux-x64.tar.gz /jdk-8u401-linux-x64.tar.g…

基于弹簧鞘复合纱和迁移学习算法的可穿戴人体重构和智能试衣系统

研究背景 在信息时代和元宇宙的背景下&#xff0c;虚拟服装设计对满足服装行业的个性化需求至关重要。与传统方法不同&#xff0c;虚拟试衣节省时间、方便客户&#xff0c;并提供多样化的款式。准确得测量人体围度并重构出人体的模型是虚拟试衣的关键。为了实现动态人体重构&a…

【面试经典 150 | 二叉树层序遍历】二叉树的右视图

文章目录 写在前面Tag题目来源解题思路方法一&#xff1a;层序遍历方法二&#xff1a;深度优先搜索 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于…

【Linux】进程和计划任务

目录 一、进程介绍 1.1 进程与线程的定义 1.1.1 进程(Process)** 1.1.2 线程(Thread)** 1.1.3 进程与线程的区别 1.2 进程的特征 1.3 进程状态 1.3.1 进程的基本状态 1.3.2 进程更多的状态 1.4 进程的优先级 1.5 进程间通信 1.6 进程的分类* 二、进程管理 2.1 查看…

SpringBoot项目创建及简单使用

目录 一.SpringBoot项目 1.1SpringBoot的介绍 1.2SpringBoot优点 二.SpringBoot项目的创建 三.注意点 一.SpringBoot项目 1.1SpringBoot的介绍 Spring是为了简化Java程序而开发的&#xff0c;那么SpringBoot则是为了简化Spring程序的。 Spring 框架&#xff1a; Spring…

【UKE!】2024.4.19

2024.4.19 【你知道的都是真相。只可惜那些并不是真相的全部。】 Friday 三月十一 谷雨 <BGM “谷雨–音阙诗听”> AC :Answer Coarse,粗劣的答案 ​ CE :Compile Easily,轻松通过 ​ PC :Perfect Compile 完美的编译 ​ WA :Wonderful Answer,好答案 ​ RE :Run Exce…

【InternLM 实战营第二期作业04】XTuner微调LLM:1.8B、多模态、Agent

基础作业 训练自己的小助手认知 1.环境安装 安装XTuner 源码 # 如果你是在 InternStudio 平台&#xff0c;则从本地 clone 一个已有 pytorch 的环境&#xff1a; # pytorch 2.0.1 py3.10_cuda11.7_cudnn8.5.0_0studio-conda xtuner0.1.17 # 如果你是在其他平台&#x…

SpringSecurity源码分析3--UserDetail部分

前言&#xff1a;本章提及的类都是与用户名、密码相关的类 UserDetailsService.class 用于加载用户信息 DaoAuthenticationProvider.class 将数据库的信息拿出来进行认证 AbstractUserDetailsAuthenticationProvider.class DaoAuthenticationProvider的父类&#xff0c;通过模…