定时器

news2025/1/1 6:19:20

定时器

定时器是什么

定时器也是软件开发中的一个重要组件. 类似于一个 “闹钟”. 达到一个设定的时间之后, 就执行某个指定好的代码.
定时器是一种实际开发中非常常用的组件.
比如网络通信中, 如果对方 500ms 内没有返回数据, 则断开连接尝试重连.
比如一个 Map, 希望里面的某个 key 在 3s 之后过期(自动删除).
类似于这样的场景就需要用到定时器.

标准库中的定时器

标准库中提供了一个 Timer 类. Timer 类的核心方法为 schedule .
schedule 包含两个参数. 第一个参数指定即将要执行的任务代码, 第二个参数指定多长时间之后执行 (单位为毫秒).

 public static void main(String[] args)
    {
        Timer timer=new Timer();
        //给timer中注册的这个任务,不是在调用schedule的线程中执行的,而是通过Timer内部的线程来负责执行的
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定时器开始执行");
            }
        },3000);//这段代码代表程序开始三秒之后开始打印出  定时器开始执行  这句话
        System.out.println("程序开始执行");
    }

结果
在这里插入图片描述
我们发现程序并没有结束,为什么呢?
是因为Timer内部有自己的线程,为了保证随时可以处理新安排的任务,这个线程会持续执行,并且这个线程还是一个前台线程

一个定时器中可以有多个任务的,定时器会根据这些任务设置的时间来合理的分配执行顺序

    public static void main(String[] args)
    {
        Timer timer=new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("3");
            }
        },3000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("2");
            }
        },2000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("1");
            }
        },1);
        System.out.println("程序开始执行");
    }

我们可以看到,虽然先给 3 分配了任务,但是任务 1 先执行了,是因为任务3的时间比任务1长

实现定时器

思路
在这里插入图片描述
优化
在这里插入图片描述

相关问题
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class MyTimerTask implements Comparable<MyTimerTask>
{
    //表示任务啥时候执行,这是一个毫秒级时间戳
    private long time;
    //表示任务具体是啥
    private Runnable runnable;
    public MyTimerTask(Runnable runnable,long delay)
    {
        //delay是一个相对的时间差,例如3000这样的数值
        //构造time是要根据当前系统时间和delay进行构造
        time=System.currentTimeMillis()+delay;
        this.runnable=runnable;
    }

    public long getTime()
    {
        return time;
    }

    public Runnable getRunnable()
    {
        return runnable;
    }

    @Override
    public int compareTo(MyTimerTask o)
    {
        //认为时间小的优先级高,最终时间最小的元素就会放到队首
        return (int)(this.time-o.time);
    }
}
//定时器类的本体
class MyTimer
{
    //用来加锁的对象
    private Object locker=new Object();
    //使用优先级队列来保证上述的N个任务
    private PriorityQueue<MyTimerTask> queue=new PriorityQueue<>();
    //定时器的核心方法,就算把要执行的任务添加到队列中
    public void schedule(Runnable runnable,long delay)
    {
        synchronized (locker)
        {
            MyTimerTask task=new MyTimerTask(runnable,delay);
            //这个集合类不是线程安全的,即会在主线程中使用,又会在扫描线程中使用,这就触发了线程安全问题,两个线程针对同一个集合作用,因此要加锁
            queue.offer(task);
            //每次来新的任务,都唤醒一下之前的扫描线程,重新规划等待时间
            locker.notify();
        }
    }
    //MyTimer中还需要构造一个”扫描线程“,一方面去负责监控队首元素是否到点了,是否应该执行
    //另一方面当任务到点之后,就要调用这里的Runnable的run方法来完成任务
    public MyTimer()
    {

        Thread t=new Thread(()->{
            while(true)
            {
                try
                {
                    synchronized (locker)
                    {
                        //扫描线程中几乎都会涉及到queue,因此全加锁
                        while(queue.isEmpty())
                        {
                            //如果当前队列为空,此时就不应该去取这里的元素
                            //continue;
                            //此处使用wait等待更合适,如果使用continue,就会使这个线程while循环运行的飞快
                            //也会陷入一个高频占用cpu的状态(忙等)
                            locker.wait();
                        }
                        MyTimerTask task=queue.peek();
                        long curTime=System.currentTimeMillis();
                        if(curTime>=task.getTime())
                        {
                            //假设当前时间是12:01,任务时间是12:00,此时就意味着要执行这个任务了
                            //执行任务之后把这个任务从队列中去除掉
                            queue.poll();
                            //执行当前这里的任务的内容
                            task.getRunnable().run();
                        }
                        else
                        {
                            //如果任务时间没到
                            //让当前扫描线程休眠一下,按照时间差来休眠
                            //Thread.sleep(task.getTime()-curTime);
                            locker.wait(task.getTime()-curTime);
                        }
                    }

                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }
}
//写一个定时器
public class Demo22
{

    public static void main(String[] args)
    {
        MyTimer timer=new MyTimer();
        timer.schedule(new Runnable() {
            @Override
            public void run()
            {
                System.out.println("任务3");
            }
        },3000);
        timer.schedule(new Runnable() {
            @Override
            public void run()
            {
                System.out.println("任务2");
            }
        },2000);
        timer.schedule(new Runnable() {
            @Override
            public void run()
            {
                System.out.println("任务1");
            }
        },1000);
        System.out.println("任务开始执行:");//
    }
}

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

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

相关文章

elasticsearch简单入门语法

基本操作 创建不同的分词器 ik_smart&#xff1a; 极简分词 &#xff1b; ik_max_word: 最细力再度分词 基本的rest命令 methodurl地址描述PUTlocalhost:9200/索引名称/类型名称/文档id创建文档&#xff08;指定文档id&#xff09;POSTlocalhost:9200/索引名称/类型名称创建文…

Qt应用开发(基础篇)——堆栈窗口 QStackedWidget

一、前言 QStackedWidget继承于QFrame&#xff0c;QFrame继承于QWidget&#xff0c;是Qt常用的堆栈窗口部件。 框架类QFrame介绍 QStackedWidget堆栈窗口&#xff0c;根据下标切换&#xff0c;一次显示一个小部件&#xff0c;常用于应用界面切换、图片轮询播放等场景。 二、QSt…

Linu网络服务NFS

linux网络服务NFS 一.NFS简介二.NFS原理三.NFS优势四.配置文件五.NFS共享存储服务的操作步骤 一.NFS简介 NFS&#xff08;网络文件服务&#xff09; NFS是一种基于tcp/ip传输的网络文件系统协议&#xff0c;最初由sun公司开放通过使用NFS协议&#xff0c;客户机可以像访问本地…

Java课题笔记~ ServletContext

单个Servlet的配置对象 web.xml <servlet><servlet-name>FirstServlet</servlet-name><servlet-class>com.ambow.test.FirstServlet</servlet-class><init-param><param-name>charset</param-name><param-value>utf-8&…

SpringMVC注解配置

1xml配置方式&#xff08;配置文件注解的方式&#xff09; 前提导入相关依赖&#xff1a;pom文件 说明&#xff1a;下方依赖是ssm项目中较为常用的一部分&#xff0c;可能部分依赖对于springmvc配置并未有关系&#xff0c;根据自己需求添加和删除。 <dependencies> &l…

新手如何快速学习单片机?

初步确定学习目标&#xff1a;是学习简单便宜的51呢&#xff0c;还是学习简单但是性价比已经不算太高的&#xff0c;但是功能强大稳定可靠的avr&#xff0c;还是物美价廉的stm32&#xff0c;或者ARM9&#xff08;可以跑系统了&#xff09;&#xff0c;再往上x86什么的如果是学8…

成员变量和局部变量的区别

局部变量成员变量 1、定义的位置不一样 在方法的内部&#xff0c;方法申明上&#xff08;形参&#xff09;。 声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量 在方法的外部&#xff0c;直接写在类当中 &#xff08;类中方法外的变量&#xff09; 2、作用范…

opencv 实现手势跟踪并返回位置信息(封装调用)

OpenCV 是一个基于 Apache2.0 许可&#xff08;开源&#xff09;发行的跨平台计算机视觉和机器学习软件库&#xff0c;可以运行在Linux、Windows、Android和Mac OS操作系统上。 需要提前准备opencv 和 mediapipe库 pip --default-timeout5000 install -i https://pypi.tuna.t…

什么是IMAP协议?

IMAP&#xff08;Internet Message Access Protocol&#xff09;是一个应用层协议&#xff0c;用于访问和管理存储在远程服务器上的电子邮件。相比于POP3&#xff0c;IMAP提供了更加丰富的功能&#xff0c;特别适用于需要在多台设备上访问电子邮件的用户。以下是关于IMAP的详细…

支持多用户协同的思维导图TeamMapper

什么是 TeamMapper &#xff1f; TeamMapper 是基于 Mindmapp 开发的用于绘制思维导图的 Web 应用程序。它使得思维导图变得简单&#xff0c;你可以托管并创建您自己的思维导图。与您的团队分享您的思维导图会议并在思维导图上进行协作。 软件特点&#xff1a; 创建&#xff1…

解决:Unexpected ‘debugger‘ statement.eslint(no-debugger) (即:页面中的 debugger 标红)的问题

1、问题描述&#xff1a; 其一、报错为&#xff1a; Unexpected debugger statement.eslint(no-debugger) 中文为&#xff1a; 意外的“调试器”语句.eslint&#xff08;无调试器&#xff09; 其二、问题描述为&#xff1a; 在正常的 vue 项目中使用 debugger 的调试过程…

人工智能原理(2)

目录 一、知识与知识表示 1、知识 2、知识表示 3、知识表示方法 二、谓词逻辑表示法 1、命题逻辑 2、谓词逻辑 三、产生式表达法 1、知识的表示方法 2、产生式系统组成 3、推理方式 4、产生式表示法特点 四、语义网络 1、概念及结构 2、语义网络的基本语义联系 …

bye 我的博客网站

Bye&#x1f64b;&#x1f64b;&#x1f64b;&#xff0c;我的博客网站。在我的服务器上运行了9个月之久的博客网站要和大家Bye了。 背景 可能很多人不知道我的这个博客网站的存在&#xff0c;好吧&#xff0c;最后一次展示它了&#xff0c;博客网站地址在这里&#xff0c;它…

空降流量危机?QQ音乐升级架构应对高并发

# 关注并星标腾讯云开发者 # 每周3 | 谈谈我在腾讯的架构设计经验 # 第2期 | 赵威&#xff1a;QQ音乐评论系统如何实现高可用&#xff1f; QQ 音乐自诞生以来&#xff0c;已有多个版本的评论业务系统。最新版本是19年再次全新迭代&#xff0c;基于 tlist 存储&#xff0c;按照发…

章节4:JavaScript操作Cookie

章节4&#xff1a;JavaScript操作Cookie 直接利用Cookie登录 JavaScript语法 获取&#xff1a;document.cookie; 设置&#xff1a;document.cookie“usernamexx”; 删除&#xff1a;document.cookie“usernamexx;expiresThu, 01 Jan 1970 00:00:00 GMT”;

Goland报错 : Try to open it externally to fix format problem

这句报错的意思也就是 : 尝试在外部打开以解决格式问题 解决方案 : 将图片格式该为.png格式&#xff0c;再粘贴进去就可以了! 改变之后的效果 : 那么&#xff0c;这样就ok了

Unity ML-Agent

介绍: 环境搭建 待为完序

javascript数据类型与引用类型的区别以及原始值详解

基本数据类型介绍 在JavaScript中,数据类型可以分为基本数据类型与引用数据类型.其中基本数据类型包括 Undefined,Null,Boolean,Number,String5种数据类型,在ES6中新增了两种基本的数据类型,Symbol,bigint 引用类型有Object,Function,Array,Date,RegExp等 这两种类型区别简略…

JavaScript高级:原型和原型链

在 JavaScript 中&#xff0c;原型与原型链是一种强大的继承机制&#xff0c;它使对象之间能够共享属性和方法&#xff0c;从而实现高效的代码复用。虽然这听起来可能有些复杂&#xff0c;但是我们可以用通俗易懂的方式来理解这个概念。本文将为你详解原型和原型链的概念与作用…

win11虚拟机安装

win11虚拟机安装 下载虚拟机客户端安装客户端创建虚拟机下载 ISO切换root账号GNOME桌面 下载虚拟机客户端 版本是16.2.3 链接&#xff1a;https://pan.baidu.com/s/13c6XVWFbeQKbCnrlfxD8cA 提取码&#xff1a;qxdc 安装客户端 安装向导 点击下一步 接收条款&#xff0c;点…