JavaEE多线程-定时器

news2025/1/16 0:45:46

目录

  • 一、定时器
    • 1.1 什么是定时器?
    • 1.2 定时器的构成
  • 二、简单实现定时器

一、定时器

1.1 什么是定时器?

定时器是多线程编码中的一个重要组件,它就好比一个闹钟,例如我们想去坐车,但是不想现在去坐车,想8:30去坐车,于是我们订了一个8点钟的闹钟,也就是说定时器有一些逻辑,但并不想立即执行,而是要等一定的时间之后再来执行.
 使用场景:浏览器访问服务器的时候就会使用定时器来判定是否超时, 标准库中其实已经提供了阻塞队列,定时器等基本组件,实际工作中我们直接使用标准库的就可以,下面的代码主要是为了理解其原理,加深对多线程的理解.

1.2 定时器的构成

  • 使用一个Task类来描述"一段逻辑",也可以说一个要执行的任务,同时也要记住这个任务在什么时候开始执行;
  • 使用一个阻塞优先队列来组织若干个Task;
  • 这个阻塞优先队列既支持阻塞的特性,又支持按优先级的"先进先出" ,使用优先级队列的目的是为了保证队首元素就是那个最早要被执行的任务,本质上就是一个堆.判断当前队列中是否有任务时间到了,只需要判断队首元素是否到时间即可.
    定时器四部分:

    1. Task类描述任务
    2. 阻塞优先队列组织任务
    3. 扫描线程,定时扫描队首元素
    4. 接口,让调用者能安排任务给定时器

    标准库中的定时器

    标准库中提供了一个 Timer 类, Timer 类的核心方法为schedule.

    Timer类构造时内部会创建线程, 有下面的四个构造方法, 可以指定线程名和是否将定时器内部的线程指定为后台线程(即守护线程), 如果不指定, 定时器对象内部的线程默认为前台线程.
    在这里插入图片描述
    schedule 方法是给Timer注册一个任务, 这个任务在指定时间后进行执行, TimerTask类就是专门描述定时器任务的一个抽象类, 它实现了Runnable接口.
    在这里插入图片描述

    import java.util.Timer;
    import java.util.TimerTask;
    
    public class Test {
        public static void main(String[] args) {
            Timer timer = new Timer();
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    System.out.println("执行延后3s的任务!");
                }
            }, 3000);
    
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    System.out.println("执行延后2s后的任务!");
                }
            }, 2000);
            
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    System.out.println("执行延后1s的任务!");
                }
            }, 1000);
        }
    }
    
    

    二、简单实现定时器

    
    //自己实现的定时器类
    class MyTimer {
        //扫描线程
        private Thread t = null;
        //阻塞队列,存放任务
        private PriorityBlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
    
        public MyTimer() {
            //构造扫描线程
            t = new Thread(() -> {
               while (true) {
                   //取出队首元素,检查队首元素执行任务的时间
                   //时间没到,再把任务放回去
                   //时间到了,就执行任务
                   try {
                       synchronized (this) {
                           MyTask task = queue.take();
                           long curTime = System.currentTimeMillis();
                           if (curTime < task.getTime()) {
                               //时间没到,放回去
                               queue.put(task);
                               //放回任务后,不应该立即就再次取出该任务
                               //所以wait设置一个阻塞等待,以便新任务到时间或者新任务来时后再取出来
                               this.wait(task.getTime() - curTime);
                           } else {
                               //时间到了,执行任务
                               task.run();
                           }
                       }
                   } catch (InterruptedException e) {
                       throw new RuntimeException(e);
    
                   }
               }
            });
            t.start();
        }
    
        /**
         * 注册任务的方法
         * @param runnable 任务内容
         * @param after 表示在多少毫秒之后执行. 形如 1000
         */
        public void schedule (Runnable runnable, long after) {
            //获取当前时间的时间戳再加上任务时间
            MyTask task = new MyTask(runnable, System.currentTimeMillis() + after);
            queue.put(task);
            //每次当新任务加载到阻塞队列时,需要中途唤醒线程,因为新进来的任务可能是最早需要执行的
            synchronized (this) {
                this.notify();
            }
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            MyTimer timer = new MyTimer();
            timer.schedule(new Runnable() {
                @Override
                public void run() {
                    System.out.println("2s后执行的任务1");
                }
            }, 2000);
    
            timer.schedule(new Runnable() {
                @Override
                public void run() {
                    System.out.println("2s后执行的任务1");
                }
            }, 1000);
        }
    }
    
    

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

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

相关文章

Linux内核驱动初探(四) 内部看门狗

目录 0. 前言 1. menuconfig 2. 设备树 3. 拓展试验 0. 前言 这次的内部看门狗驱动也比较顺利&#xff0c;重点看了 原理图和4.19.x 内核的配置。 内部看门狗设备名叫做 /dev/watchdog 。 1. menuconfig 我们在 linux-menuconfig 里面如下设置&#xff1a;进入 Device D…

[Java]JavaWeb学习笔记(动力节点老杜2022)

文章目录&#x1f97d; Tomcat服务器&#x1f30a; 下载与安装&#x1f30a; 关于Tomcat服务器的目录&#x1f30a; 启动Tomcat&#x1f30a; 实现一个最基本的web应用&#xff08;这个web应用中没有java小程序&#xff09;&#x1f97d; 静态资源与动态资源&#x1f97d; 模拟…

GPU虚拟化(留坑)

文章内容大程度参考B站王利明老师对《GPU虚拟化技术分享》的演讲&#xff1a;https://b23.tv/uQKBpcK GPU 有什么用&#xff1f; GPU可以用于图形渲染&#xff0c;也能够用于高性能计算和编解码等场景。 图&#xff1a;GPU 的典型软件架构&#xff08;不含虚拟化&#xff09; …

注解存储对象到Spring,详解 五大类注解 和方法注解

上一篇博客我们介绍了如何使用xml来引入bean对象&#xff0c;当项目多的时候&#xff0c;显然那样是比较麻烦的。现在我们只需要 个注解就可以替代了。注意&#xff1a;注解和xml可以同时使用准备工作:配置扫描路径我们需要配置 下存储对象的扫描包路径&#xff0c;只有被配置的…

【笔记】openwrt - full cone NAT(全锥NAT)、解决“arp_cache: neighbor table overflow!”

最近安装了比特彗星&#xff08;bitcomet&#xff09;后&#xff0c;老是收到警告说日志的接收超过每秒上限了。一看日志&#xff0c;好家伙&#xff0c;一堆的kern.info kernel: [194004.157620] neighbour: arp_cache: neighbor table overflow!日志&#xff0c;还是kernel的…

损失函数总结

回归损失与分类损失 回归用于逼近某个数值,预测的结果是连续的,例如预测小明的身高,160,161,162,163cm。平方损失即MSE: 分类用于预测物体属于某一个标签,预测的结果是离散的,例如预测明天是否下雪:是or否。 由于预测分类,最终的输出需要介于(0,1)之间,通常在网络…

Redis消息队列 | 黑马点评

目录 一、认识消息队列 二、List模拟消息队列 三、PubSub的消息队列 四、Stream的消息队列&#xff08;重点&#xff09; 1、单消费模式 2、消费者组 五、redis三种消息队列对比 六、优化秒杀实战 1、创建消息队列 2、修改下单脚本 3、接收消息处理 一、认识消息队列 …

设计模式 - 创建型模式_工厂方法模式

文章目录创建型模式概述CaseBad ImplBetter Impl &#xff08;⼯⼚模式优化代码&#xff09;创建型模式 创建型模式提供创建对象的机制&#xff0c; 能够提升已有代码的灵活性和可复⽤性。 类型实现要点工厂方法定义⼀个创建对象的接⼝&#xff0c;让其⼦类⾃⼰决定实例化哪⼀…

【蓝桥杯-筑基篇】基础数学思维与技巧(1)

&#x1f353;系列专栏:蓝桥杯 &#x1f349;个人主页:个人主页 目录 1.一百以内的AB 2.小学生算术求进位次数 3.最大公约数 4.最小公倍数 5.十进制转换其他进制 6.其他进制转十进制 7.天空数 8.求集合的所有子集 9.判断一个数是否为2的次方数 10.二进制中1的个数 1.一…

ISIS简介、NSAP与NET地址、Router-Id转换成NET地址

2.0.0 ISIS简介、NSAP与NET地址、Router-Id转换成NET地址 ISIS简介 IS-IS&#xff08;Intermediate System-to-Intermediate System&#xff09;中间系统到中间系统。 1、该协议最初是ISO国际标准化组织为CLNP&#xff08;Connection Less Network Protocol&#xff0c;无连接…

HashMap 正解

HashMap 实现原理 以及扩容机制 HashMap 的 put 以及扩容基本实现 数据结构 上述截图是 HashMap 的内部存储的数据结构。大体上是通过 hash 值来获取到对应的下标。如果当前下标为 null 的话&#xff0c;直接创建并设置一个新的节点&#xff0c;反之就是添加到该链表的最后 pu…

好客租房-09_学习MongoDB并完善通讯系统

9. 学习MongoDB 并完善租房的通讯系统后端本章目的为MongoDB快速入门, 并完善上一节编写的通讯系统后台, 将DAO层从HashMap迁移到MongoDB中.思考如下问题:MongoDB属于关系型还是非关系型数据库为什么在我们的通讯系统中选择MongoDB作为数据库?9.1 mongoDB概念简介MongoDB是一个…

python+django医院固定资产设备管理系统

管理员功能模块 管理员登录&#xff0c;通过填写用户名、密码、角色等信息&#xff0c;输入完成后选择登录即可进入医院设备管理系统&#xff0c; 管理员登录进入医院设备管理系统可以查看首页、个人中心、科室员管理、维修员管理、设备领用管理、设备信息管理、设备入库管理、…

人工智能入门杂记

本篇文章属于所有发表的文章的导读吧&#xff0c;以后会常更新。 目录 1.数据挖掘、机器学习、深度学习、云计算、人工智能 2.深度学习、强化学习、对抗学习、迁移学习 3.基础知识--线性代数 4.基础知识--概率与数理统计 5.常用工具库 6.机器学习 6.1 什么是训练什么是推…

Java数组

文章目录Java 数组一、数组介绍二、数组1. 数组静态初始化1.1 数组定义格式1.2 数组静态初始化2. 数组元素访问3. 数组遍历操作3.1 数组遍历介绍3.2 数组遍历场景3.3 数组遍历案例1&#xff09;数组遍历-求偶数和2&#xff09;数组遍历-求最大值3&#xff09;数组遍历综合案例4…

【C语言航路】第十四站:文件

目录 一、为什么使用文件 二、什么是文件 1.程序文件 2.数据文件 3.文件名 三、文件的打开和关闭 1.文件指针 2.文件的打开和关闭 四、文件的顺序读写 1.对于输入输出的理解 2.fgetc与fputc &#xff08;1&#xff09;fgetc与fputc的介绍 &#xff08;2&#xff0…

2023年springcloud面试题(第一部分)

1. 什么是微服务架构微服务架构就是将单体的应用程序分成多个应用程序&#xff0c;这多个应用程序就成为微服务&#xff0c;每个微服务运行在自己的进程中&#xff0c;并使用轻量级的机制通信。这些服务围绕业务能力来划分&#xff0c;并通过自动化部署机制来独立部署。这些服务…

MP-4可燃气体传感器介绍

MP-4可燃气体传感器简介MP-4可燃气体传感器采用多层厚膜制造工艺&#xff0c;在微型Al2O3陶瓷基片的两面分别制作加热器和金属氧化物半导体气敏层&#xff0c;封装在金属壳体内。当环境空气中有被检测气体存在时传感器电导率发生变化。该气体的浓度越高&#xff0c;传感器的电导…

JavaWeb | JDBC相关API详解 2 (内附以集合形式输出表)

本专栏主要是记录学习完JavaSE后学习JavaWeb部分的一些知识点总结以及遇到的一些问题等&#xff0c;如果刚开始学习Java的小伙伴可以点击下方连接查看专栏 本专栏地址&#xff1a;&#x1f525;JDBC Java入门篇&#xff1a; &#x1f525;Java基础学习篇 Java进阶学习篇&#x…

C语言编程题

1、求斐波那契数列1&#xff0c;1&#xff0c;2&#xff0c;3&#xff0c;5&#xff0c;8……前20项之和 #include<stdio.h> int main() {int i,j,k,t2;ij1;printf("%d %d\n",i,j);for(k0;k<9;k){iij;jij;ttij;printf("%d %d\n",i,j);}printf(&q…