【JavaEE】定时器的简单实现

news2024/9/21 20:39:18

目录

定时器

实现定时器

描述任务

保存任务

扫描任务

执行任务


定时器

在实现定时器之前,先来简单的了解一下什么是定时器。

定时器是软件开发中一个重要的组件。比如到了什么时候,干一件什么样的事情;多少秒之后干什么。本篇文章介绍的多长时间后干什么事情。

Java标准库中的定时器,所提供的类——Timer  核心方法是schedule()。这里只演示多久之后执行任务这个方法。

import java.util.Timer;
import java.util.TimerTask;

public class ThreadDemo25 {

    public static void main(String[] args) {

        Timer timer = new Timer();

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("这是任务1, 1000ms后执行");
            }
        }, 1000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("这是任务2, 2000ms后执行");
            }
        }, 2000);

        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                System.out.println("这是任务3,3000ms后执行");
            }
        };
        timer.schedule(task, 3000);
    }

}

这里schedule中的两个参数

一个是TimeTask task  描述具体要执行的任务

一个是long time  描述time时间后执行该任务

其中TimeTask是一个抽象类,实现了Runnable接口。可以简单的理解为是堆Runnable接口的升级,实际上还是Runnable接口。


实现定时器

实现的是多久之后执行任务这一定时器。

描述任务

这里需要一个类是描述这个任务,因为除了任务之外,还需要时间。

// 使用Task类描述任务
// 因为存储该任务使用的是优先级阻塞队列,要比出优先级需要有比较器
// 这里直接实现Comparable接口
class Task implements Comparable<Task> {
    // 时间
    private long after;
    // 任务
    private Runnable runnable;

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

    @Override
    public int compareTo(Task task) {
        return (int)(this.after - task.after);
    }

    public long getAfter() {
        return after;
    }

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

保存任务

这里使用PriorityBlockingQueue来保存任务。因为保存进去的时候会按照时间来保存。带有优先级的阻塞队列完美符合这一点。检查是否要执行任务,只需要查看队首的元素时间是不是到了。

    // 使用优先级阻塞队列保存任务
    private PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();

扫描任务

定时器创建时(所以需要在构造方法中),需要一个线程一直扫描检查里面的任务时间是否到了,到了就立马执行。

    // 扫描线程时间
    private Thread scan = null;

执行任务

执行任务根据时间来判断是否要执行。如果时间还没到的时候,那么线程应该阻塞才好。其中时间是不固定的。可能随时会有新的任务添加进来,这样就会导致时间的不确定性。这里使用wait和notify来让任务阻塞还是执行。如果有新的任务添加进来,就唤醒以下正在阻塞的扫描线程,看看新的时间是否要执行,如果时间还不到,就继续阻塞,知道最早的时间到了,在执行任务。

    public MyTimer() {
        // 创建一个线程扫描任务时间
        scan = new Thread(() -> {
            while (true) {
                try {
                    long now = System.currentTimeMillis();
                    Task task = queue.take();
                    // 如果最快执行的任务的时间大于当前时间,就wait二者相差时间
                    // 否则将会一直取出来在放进去
                    if (task.getAfter() > now) {
                        queue.put(task);
                        synchronized (locker) {
                            locker.wait(task.getAfter() - now);
                        }
                    } else {
                        task.run();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        scan.start();
    }

    public void schedule (Runnable runnable, long after){
        Task task = new Task(runnable, System.currentTimeMillis() + after);
        queue.put(task);
        synchronized (locker) {
            locker.notify();
        }
    }

完整代码如下

// 实现一个定时器

import java.util.concurrent.PriorityBlockingQueue;

// 使用Task类描述任务
// 因为存储该任务使用的是优先级阻塞队列,要比出优先级需要有比较器
// 这里直接实现Comparable接口
class Task implements Comparable<Task> {
    // 时间
    private long after;
    // 任务
    private Runnable runnable;

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

    @Override
    public int compareTo(Task task) {
        return (int)(this.after - task.after);
    }

    public long getAfter() {
        return after;
    }

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

class MyTimer {
    // 扫描线程时间
    private Thread scan = null;

    // 使用优先级阻塞队列保存任务
    private PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();

    // 时间还没有到的时候
    // 使用这个对象进行加锁和唤醒
    private Object locker = new Object();

    // 构造方法
    public MyTimer() {
        // 创建一个线程扫描任务时间
        scan = new Thread(() -> {
            while (true) {
                try {
                    long now = System.currentTimeMillis();
                    Task task = queue.take();
                    // 如果最快执行的任务的时间大于当前时间,就wait二者相差时间
                    // 否则将会一直取出来在放进去
                    if (task.getAfter() > now) {
                        queue.put(task);
                        synchronized (locker) {
                            locker.wait(task.getAfter() - now);
                        }
                    } else {
                        task.run();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        scan.start();
    }

    public void schedule (Runnable runnable, long after){
        Task task = new Task(runnable, System.currentTimeMillis() + after);
        queue.put(task);
        synchronized (locker) {
            locker.notify();
        }
    }
}

public class ThreadDemo26 {
    public static void main(String[] args) {
        MyTimer myTimer = new MyTimer();
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("这是任务1");
            }
        }, 6000);

        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("这是任务2");
            }
        }, 2000);
    }
}

 


有什么问题评论区指出。希望可以帮到你。 

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

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

相关文章

活动星投票最美养生师展网络评选微信的投票方式线上免费投票

“最美养生师”网络评选投票_用户同什么方法挑选投票小程序_最好的投票小程序用户在使用微信投票的时候&#xff0c;需要功能齐全&#xff0c;又快捷方便的投票小程序。而“活动星投票”这款软件使用非常的方便&#xff0c;用户可以随时使用手机微信小程序获得线上投票服务&…

Hive函数大全–完整版(三)

官网参考地址&#xff1a; 官网UDF - Apache Hive 1. 基本数据类型 2. 基础运算符与函数 SQL结果A IS NULL 空A IS NOT NULL 非空 A LIKE B 模糊匹配A RLIKE B 正则表达式匹配A REGEXP B 正则表达式匹配 3. 类型转换 cast(expr as <type>)…

园区网典型组网架构及案例实践

什么是园区网园区网络是限定区域内&#xff0c;连接人与物的局域网络&#xff1b;园区网络通常只有一个管理主体&#xff1b;如果有多个管理主体&#xff0c;通常被认为为多个园区网络。园区网络典型架构小型园区网络典型架构小型园区网络应用于接入用户数量较少的场景&#xf…

SpringBoot 统一功能处理

SpringBoot 统一功能处理前言一、用户登录权限效验1.1 最初的用户登录验证1.2 Spring AOP 用户统一登录验证的问题1.3 Spring 拦截器1.3.1 准备工作1.3.2 自定义拦截器1.3.3 将自定义拦截器加入到系统配置1.4 拦截器实现原理1.4.1 实现原理源码分析1.4.2 拦截器小结1.5 扩展&am…

CBC模式的3DES加解密(课程设计报告)

目录一、实验内容二、实验原理2.1 DES加解密原理2.1.1 DES加解密的基本原理2.1.2 DES加解密的关键步骤2.2 3DES加解密原理2.3 分组密码CBC加解密模式原理2.4 填充原理三、实验过程3.1 变量说明3.1.1 主函数变量说明3.1.2 其他重要变量说明3.2 函数功能说明3.2.1主函数说明3.2.2…

并行训练方法-单机多卡

一、简单方便的 nn.DataParallel DataParallel 可以帮助我们&#xff08;使用单进程控&#xff09;将模型和数据加载到多个 GPU 中&#xff0c;控制数据在 GPU 之间的流动&#xff0c;协同不同 GPU 上的模型进行并行训练&#xff08;细粒度的方法有 scatter&#xff0c;gather …

学习记录670@项目管理之变更管理

变更的分类 按变更性质分为重大变更、重要变更和一般变更&#xff0c;可通过不同审批权限控制。按变更的迫切性分为紧急变更和非紧急变更&#xff0c;可通过不同的变更处理流程进行控制。按变更所发生的领域和阶段&#xff0c;可分为进度变更、成本变更、质量变更、设计变更、…

3小时精通opencv(四) 透视变换与图像拼接

3小时精通opencv(四) 透视变换与图像拼接 参考视频资源:3h精通Opencv-Python 文章目录3小时精通opencv(四) 透视变换与图像拼接透视变换图像拼接全部代码透视变换 透视变换建立两平面场之间的对应关系&#xff0c; 将原始图片投影到一个新的视平面上 # Author : JokerTon…

Elasticsearch7.8.0版本入门——JavaAPI操作(环境准备)

目录一、创建springboot项目二、pom.xml文件引入相关maven依赖三、创建客户端对象一、创建springboot项目 创建springboot项目步骤参考此博文链接&#xff1a;https://wwwxz.blog.csdn.net/article/details/91977374 二、pom.xml文件引入相关maven依赖 引入elasticsearch依赖…

NR PUSCH(七) 相干传输

微信同步更新欢迎关注同名mode协议笔记 这篇就是为记录一个概念在协议中的体现方式。相干传输被定义为一种UE能力。考虑到UE的实现成本&#xff0c;NR不要求所有的UE都能做到所有的天线端口都可以相干传输。NR定义了以下3种UE的相干传输能力。 (1)全相干&#xff08;Fully-coh…

正点原子Linux驱动第三期

目录 第一讲 系统镜像烧写 第二讲 u-boot编译 第三讲 uboot命令使用 第四讲 Uboot源码分析 第五讲 uboot顶层 Makefile分析 第六讲 Uboot启动流程 第七讲 uboot移植 第八讲 UBoot图形化界面配置 第九讲 Linux 内核移植 第十讲 Linux内核源码目录分析 第十一讲 Linux…

六、python操作mysql篇(黑马程序猿-python学习记录)

黑马程序猿的python学习视频&#xff1a;https://www.bilibili.com/video/BV1qW4y1a7fU/ 目录 1. 下载pymysql 2. 新建数据库 3. mysql服务器版本查询 4. 执行非查询性质的SQL 5. 执行查询性质的sql 6. 执行新增sql 1. 下载pymysql 右下角点击版本 选择解释器设置 点击加号 搜…

Python(for和while)循环嵌套及用法

Python 不仅支持 if 语句相互嵌套&#xff0c;while 和 for 循环结构也支持嵌套。所谓嵌套&#xff08;Nest&#xff09;&#xff0c;就是一条语句里面还有另一条语句&#xff0c;例如 for 里面还有 for&#xff0c;while 里面还有 while&#xff0c;甚至 while 中有 for 或者 …

无需下载就能使用的图像编辑器?能代替 Photoshop 吗?#Photopea

一款在线就能使用的图像编辑器&#xff0c;好用&#xff1f;还免费&#xff1f;Photopea 图源: Photopea 官网Photopea 可以处理光栅图形和矢量图形。您可以将它用于简单的任务&#xff0c;例如调整图像大小&#xff0c;也可以用于复杂的任务&#xff0c;例如设计网页、创建插图…

Day05 - 内置函数和参数

0. 列表 推导式 格式: 列表变量 [表达式 for 变量 in range(10)] 表达式中需要使用后面的变量 列表推导 式 创建一个具有一百个数字的列表 # c_l [] # for i in range(100): # c_l.append(i)# 使用列表推导式来完成列表的创建 c_l [i for i in range(100)] # c_l [x f…

macOS Ventura 13.2 (22D49) 正式版发布,ISO、IPSW、PKG 下载

macOS Ventura 13 正式版现已发布&#xff01; 请访问原文链接&#xff1a;https://sysin.org/blog/macOS-Ventura/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 2023 年 1 月 23 日&#xff08;北京时间 24 日凌晨&#xff09;&#xff0c;macOS Ventura 1…

新年快乐—数睿通2.0数据中台全新功能模块发布

文章目录引言数据集成数据库管理文件管理数据接入贴源数据数据开发数据生产sql 作业开发FlinkSql 作业开发调度中心运维中心资源中心配置中心其他模块结语引言 离上次发文已经有接近三个月了&#xff0c;这三个月主要在开发数睿通的数据生产模块&#xff0c;同时优化了一下数据…

网络实验之EIGRP协议

一、EIGRP协议简介 EIGRP:Enhanced Interior Gateway Routing Protocol 即 增强内部网关路由协议。也翻译为 加强型内部网关路由协议。 EIGRP是Cisco公司的私有协议&#xff08;2013年已经公有化&#xff09;。 EIGRP结合了链路状态和距离矢量型路由选择协议的Cisco专用协议&am…

【LeetCode】二叉树的序列化与反序列化 [H](二叉树)

297. 二叉树的序列化与反序列化 - 力扣&#xff08;LeetCode&#xff09; 一、题目 序列化是将一个数据结构或者对象转换为连续的比特位的操作&#xff0c;进而可以将转换后的数据存储在一个文件或者内存中&#xff0c;同时也可以通过网络传输到另一个计算机环境&#xff0c;采…

【SpringCloud】Nacos注册中心、配置中心用法与原理(下)

【SpringCloud】Nacos注册中心、配置中心用法与原理&#xff08;下&#xff09; 上集回顾 二、Nacos 配置中心 1. 统一配置管理 &#xff08;1&#xff09;在 Nacos 中添加配置文件 &#xff08;2&#xff09;从微服务拉取配置 2. 配置热更新 方式一&#xff1a;使用 Re…