[JAVAee]定时器

目录

定时器的含义

定时器的使用

定时器的解析

①TaskQueue

​②TimerThread

③Timer

定时器的模拟实现

①创建Task自定义类型

②创建TimerThread类

③Timer类

完整代码


定时器的含义

从名字上看,就是我们通俗理解的那个定时器.设置一定的时间,并在一定的时间后发生一定的操作.

定时器的使用

在java标准库中有一个Timer类实现了这个定时器

创建出Timer的实例后,使用Timer类中的schedule方法往其添加任务.

方法说明

public void schedule(TimerTask task, long delay)

在指定的延迟之后安排指定的任务执行。

关于第一个参数TimerTask类,其是继承了我们先前在多线程中Thread类中提到过了Runnable类.两者的使用方法和效果其实是大致相同的.

也要在TimerTask类中覆写run方法,说明要执行的任务.

值得我们注意的是,可以创建一个定时器实例后.不断了往里添加新的任务.

而不是一个定时器中只能包含有一个任务

Timer timer = new Timer();//创建一个实例对象
//调用schedule方法,并通过TimerTask的匿名内部类的方式覆写了其中的run方法
//在2秒后打印出"wow"与打印的时间点
timer.schedule(new TimerTask() {
     @Override
     public void run() {
         System.out.println("wow " + new Date());
     }
},2000);
//与上一致,在1秒后打印出"haha"与打印的时间点
timer.schedule(new TimerTask() {
    @Override
    public void run() {
        System.out.println("haha " + new Date());
    }
},1000);

定时器的解析

在java中,我们可以看到Timer类中主要为这三个类

①TaskQueue

其中TaskQueue是一个优先级队列,也称为堆.是一个以执行时间排序的小根堆.

关于使用优先级队列的原因:

时间本身就是一个很强烈的拥有前后顺序的一个数据.

假设11:00,12:00与13:00的时段分别设定一个定时器分别拥有三个任务.

在11:00时段还没有到达的时候,后面两个任务是永远永远不会先轮到他们启动的,所以对于在定时器中查看是否满足时间条件执行任务可以只观察第一个存放的数据,而不是每次都要将存储的数据全部遍历一遍来查看是否要执行相应的任务,达到了更高效的效果.

②TimerThread

在TimerThread类中,会创建出新的线程来负责检测扫描TaskQueue队列中的任务是否需要执行了.

当队列为空的时候,会调用wait方法变更为阻塞状态.

③Timer

在此类中,存放的就是Timer的构建方法,schedule方法等的实现.还会有一个标志位,判断queue队列中是否为空.

当队列为空时,TimerThread线程使用了wait方法为阻塞状态,在Timer中添加了新的数据后随即会调用相应的notify方法唤醒线程,开始扫描queue队列.

定时器的模拟实现

经过了一定解析后,我们对定时的模拟可以分成:

  1. 要有存放任务数据的数据结构
  2. 要有一个线程来负责扫描数据结构中存放的数据

因为是在定时器的实现是在多线程的环境下的,main主线程一个,自身也有一个TimerThread线程.

为了保证线程安全,我们数据结构可以使用阻塞队列来进行模拟实现.

①创建Task自定义类型

在此类中,实现的是任务类型Task的构造方法

class Task implements Comparable<Task>{
        private Runnable runnable;//使用Runnable类可以达到TimerTask的效果,都是存放定时器中要执行的任务
        private long time;//来记录任务执行的时间点

        public Task(Runnable runnable,long time){//重写Task的构造方法
            this.runnable = runnable;//创建新的Task时,要重写Runnable.
            this.time = System.currentTimeMillis() + time;//因为放入参数的时间都是一个时间段,而我们需要是时间点
        }
        public void run(){//启动任务
            runnable.run();
        }
        @Override
        public int compareTo(Task o) {//自定义类型放入队列中,要记得重写比较方法噢
            return (int)(this.time - o.time);
        }
    }

②创建TimerThread类

实现对存放Task的优先级阻塞队列的扫描

private PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();
    private Object object = new Object();
    class TimerThread extends Thread{
        @Override
        public void run() {
            while(true){
                try {
                    Task task = queue.take();//从队列中取出队首元素
                    long curTime = System.currentTimeMillis();
                    if(task.time > curTime){
                        //如果还没队首任务的时间则wait一段时间
                        //并把取出查看的任务再塞回队列
                        //要记住我们存放的数据结构是优先级队列,会自动排序噢
                        queue.put(task);
                        synchronized (object){
                            //wait记得搭配synchronized使用
                            //wait的时间为,当前时间到队首任务的时间之差
                            wait(task.time - curTime);
                            //这里的wait有了一段明确的时间,是不需要notify的
                        }
                    }else{
                        //如果时间已经到了,就可以运行任务啦
                        task.getRun();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break;//可以在这里写上break,因为while是true循环.
                    //加上break,中断后可以直接跳出循环,就不会卡死了.
                }
            }

        }
    }

③Timer类

实现schedule方法

public class Timer {
    public Timer(){//构造方法
        TimerThread thread = new TimerThread();//扫描线程
        thread.start();//并启动
    }
    public void schedule(Runnable runnable,long time){//schedule方法,往队列中添加任务
        Task task = new Task(runnable,time);//创建Task类型的任务
        queue.put(task);//往队列中加入任务
        synchronized (object) {//在加入任务后,需要先唤醒线程.
            //此时线程可能因为还没到队首元素的时间调用了wait方法还在阻塞
            //新加入的任务的时间可能比队列中队首元素的任务的时间要早
            //所以需要唤醒线程重新扫描一遍队列,查看是否达到了时间
            object.notify();
        }
    }
}

完整代码

import java.util.concurrent.PriorityBlockingQueue;

public class Timer{
    class Task implements Comparable<Task>{
        Runnable runnable;
        long time;

        public Task(Runnable runnable,long time){
            this.runnable = runnable;
            this.time = time;
        }

        public void getRun(){
            runnable.run();
        }

        @Override
        public int compareTo(Task o) {
            return (int)(this.time - o.time);
        }
    }
    ///
    private PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();
    private Object object = new Object();
    class TimerThread extends Thread{
        @Override
        public void run() {
            while(true){
                try {
                    Task task = queue.take();//从队列中取出队首元素
                    long curTime = System.currentTimeMillis();
                    if(task.time > curTime){
                        //如果还没队首任务的时间则wait一段时间
                        //并把取出查看的任务再塞回队列
                        //要记住我们存放的数据结构是优先级队列,会自动排序噢
                        queue.put(task);
                        synchronized (object){
                            //wait记得搭配synchronized使用
                            //wait的时间为,当前时间到队首任务的时间之差
                            wait(task.time - curTime);
                            //这里的wait有了一段明确的时间,是不需要notify的
                        }
                    }else{
                        //如果时间已经到了,就可以运行任务啦
                        task.getRun();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break;//可以在这里写上break,因为while是true循环.
                    //加上break,中断后可以直接跳出循环,就不会卡死了.
                }
            }

        }
    }
    /
    public Timer(){
        TimerThread thread = new TimerThread();
        thread.start();
    }
    public void schedule(Runnable runnable,long time){
        Task task = new Task(runnable,time);
        queue.put(task);
        synchronized (object) {
            object.notify();
        }
    }

    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("wow");
            }
        },1000);
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hahaha");
            }
        },2000);
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

01 矩阵(力扣)多源广度优先搜索 JAVA

给定一个由 0 和 1 组成的矩阵 mat &#xff0c;请输出一个大小相同的矩阵&#xff0c;其中每一个格子是 mat 中对应位置元素到最近的 0 的距离。 两个相邻元素间的距离为 1 。 输入&#xff1a;mat [[0,0,0],[0,1,0],[0,0,0]] 输出&#xff1a;[[0,0,0],[0,1,0],[0,0,0]] 输入…

代理模式--静态代理和动态代理

1.代理模式 定义&#xff1a;代理模式就是代替对象具备真实对象的功能&#xff0c;并代替真实对象完成相应的操作并且在不改变真实对象源代码的情况下扩展其功能&#xff0c;在某些情况下&#xff0c;⼀个对象不适合或者不能直接引⽤另⼀个对象&#xff0c;⽽代理对象可以在客户…

CentOS 8 错误: Error setting up base repository

配置ip、掩码、网关、DNS VMware网关可通过如下查看 打开网络连接 配置镜像的地址 vault.centos.org/8.5.2111/BaseOS/x86_64/os/

python 面向对象编程的特点 - 封装 - 继承(经典类、新式类) - 多态 - 静态方法、类方法 - 下划线的使用 - 回合制攻击游戏实验

目录 面向对象编程的特点&#xff1a; 封装&#xff1a;封装是将数据和操作&#xff08;方法&#xff09;封装在一个对象中的能力 继承&#xff1a;继承是指一个类&#xff08;子类&#xff09;可以继承另一个类&#xff08;父类&#xff09;的属性和方法。 我们为什么需要继…

PostgreSQL构建时间

– PostgreSQL构建时间 select make_timestamp(2023,7,27,7,34,16);

Ubuntu—vi编辑器的使用一

vi编辑器 vi是Linux中最基本的编辑器。但vi编辑器在系统管理、服务器配置工作中永远都是无可替代的。 vi编辑器的使用 vi有以下三种模式 命令行模式 用户在用vi编辑文件时&#xff0c; 最初进入的是该模式。可以进行复制、粘贴等操作 插入模式 进行文件编辑&#xff0c; 按…

Java的第十五篇文章——网络编程(后期再学一遍)

目录 学习目的 1. 对象的序列化 1.1 ObjectOutputStream 对象的序列化 1.2 ObjectInputStream 对象的反序列化 2. 软件结构 2.1 网络通信协议 2.1.1 TCP/IP协议参考模型 2.1.2 TCP与UDP协议 2.2 网络编程三要素 2.3 端口号 3. InetAddress类 4. Socket 5. TCP网络…

VMPWN的入门系列-2

温馨提示&#xff1a; 文章有点长&#xff0c;图片比较多&#xff0c;请耐心阅读 实验四 VMPWN4 题目简介 这道题应该算是虚拟机保护的一个变种&#xff0c;是一个解释器类型的程序&#xff0c;何为解释器&#xff1f;解释器是一种计算机程序&#xff0c;用于解释和执行源代码。…

PKG内容查看工具:Suspicious Package for Mac安装教程

Suspicious Package Mac版是一款Mac平台上的查看 PKG 程序包内信息的应用&#xff0c;Suspicious Package Mac版支持查看全部包内全部文件&#xff0c;比如需要运行的脚本&#xff0c;开发者&#xff0c;来源等等。 suspicious package mac使用简单&#xff0c;只需在选择pkg安…

螺旋矩阵 II

给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;[[1,2,3],[8,9,4],[7,6,5]] 示例 2&#xff1a; 输入&#xff1a;n 1 输出&a…

软工导论知识框架(二)结构化的需求分析

本章节涉及很多重要图表的制作&#xff0c;如ER图、数据流图、状态转换图、数据字典的书写等&#xff0c;对初学者来说比较生僻&#xff0c;本贴只介绍基础的轮廓&#xff0c;后面会有单独的帖子详解各图表如何绘制。 一.结构化的软件开发方法&#xff1a;结构化的分析、设计、…

【并发编程】ForkJoinPool工作原理分析

目录 前置内容课程内容一、由一道算法题引发的思考1.算法题2.什么是归并排序法 二、什么是Fork/Join框架1.基本介绍2.ForkJoinPool2.ForkJoinPool构造函数及参数解读3.任务提交方式4.工作原理图5.工作窃取6.和普通线程池之间的区别7.ForkJoinTask 学习总结 前置内容 Q1&#x…

WEB:web2

背景知识 代码审计 题目 由上述可知&#xff0c;这段代码定义了一个函数encode&#xff0c;接受一个字符串参数$str&#xff0c;并返回对其进行加密后的结果 加密算法包括&#xff1a; 使用strrev函数将字符串进行翻转&#xff1b;对翻转后的每个字符&#xff0c;将其ASCII值…

helm部署rabbitmq

1.添加rabbitmq仓库并下载包 helm repo add bitnami https://charts.bitnami.com/bitnami helm pull bitnami/rabbitmq --version 10.1.4 tar -zxvf rabbitmq-10.1.4.tgz mv values.yaml values.yaml.back grep -v "#" values.yaml.back > values.yaml2.helm部署…

xxl-Job分布式任务调度

1.概述 1.1 什么是任务调度 我们可以先思考一下业务场景的解决方案&#xff1a; 某电商系统需要在每天上午10点&#xff0c;下午3点&#xff0c;晚上8点发放一批优惠券。某银行系统需要在信用卡到期还款日的前三天进行短信提醒。某财务系统需要在每天凌晨0:10结算前一天的财…

系统架构设计师-软件架构设计(3)

目录 一、软件架构风格&#xff08;其它分类&#xff09; 1、闭环控制结构&#xff08;过程控制&#xff09; 2、C2风格 3、MDA&#xff08;模型驱动架构 Model Driven Architecture&#xff09; 4、特定领域软件架构&#xff08;DSSA&#xff09; 4.1 DSSA基本活动及产出物…

MySQL之深入InnoDB存储引擎——Checkpoint机制

文章目录 一、引入二、LSN三、触发时机 一、引入 由于页的操作首先都是在缓冲池中完成的&#xff0c;那么如果一条DML语句改变了页中的记录&#xff0c;那么此时页就是脏的&#xff0c;即缓冲池中页的版本要比磁盘的新。那么数据库需要将新版本的页刷新到磁盘。倘若每次一个页…

Unity源码分享-黄金矿工游戏完整版

Unity源码分享-黄金矿工游戏完整版 项目地址&#xff1a;https://download.csdn.net/download/Highning0007/88118933

Raki的读paper小记:RWKV: Reinventing RNNs for the Transformer Era

Abstract&Introduction&Related Work 研究任务 基础模型架构已有方法和相关工作 RNN&#xff0c;CNN&#xff0c;Transformer稀疏注意力&#xff08;Beltagy等人&#xff0c;2020年&#xff1b;Kitaev等人&#xff0c;2020年&#xff1b;Guo等人&#xff0c;2022年&am…

arm 函数栈回溯

大概意思就是arm每个函数开始都会将PC、LR、SP以及FP四个寄存器入栈。 下面我们看一下这四个寄存器里面保存的是什么内存 arm-linux-gnueabi-gcc unwind.c -mapcs -w -g -o unwind&#xff08;需要加上-mapcs才会严格按照上面说的入栈&#xff09; #include <stdio.h> …