【JavaEE】多线程案例 - 定时器

news2024/11/29 10:57:46

作者主页:paper jie_博客

本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。

本文于《JavaEE》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力)打造,将MySQL基础知识一网打尽,希望可以帮到读者们哦。

其他专栏:《MySQL》《C语言》《javaSE》《数据结构》等

内容分享:本期将会分享多线程案例 - 定时器

目录

 什么是定时器

Java标准库中的定时器 - Timer

自定义一个定时器

定时器的组成

描述类MyTask

实现MyTask的比较

MyTimer类的构架

schedule方法

内置线程

线程安全与wait等待

具体代码

代码执行流程


 什么是定时器

定时器是我们程序猿来软件开发中一个很重要的组件.它的作用就是和闹钟一样. 当达到一个设定的时间后,就需要去执行某段指定的代码.定时器在我们实际开发中特别常见.比如一款游戏需要联网才能使用,要是在1秒之内没有数据返回给服务器,这时定时器就会发挥作用,断开与网络的连接然后尝试重连.

Java标准库中的定时器 - Timer

在我们Java标准库中就内置了一个Timer类,他就是定时器. 它里面有一个核心方法schedule就是用来注册任务的. 

schedule里面有两个参数. 一个是时间到了需要执行的代码, 第二个是需要等待的时间,单位是毫秒.

public class ThreadDemo11 {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello 1000");
            }
        }, 1000);
    }
}

自定义一个定时器

定时器的组成

这里用一个MyTimer类来表示定时器

1) 需要一个存放任务的优先级队列PriorityQueue

2) 需要一个MyTask类来描述任务

3) 这些任务是需要比较时间的,MyTask类需要实现Comparable接口

4) 需要定义一个内置线程来不断扫描任务观察时间到了没

5) 需要实现核心方法schedule来注册任务

描述类MyTask

Task这个类用来描述任务,里面包含一个Runnable对象和一个time时间戳. 需要执行的代码会通过传参的形式给到Runnable.

class MyTask3 {
    //
    private long time;
    private Runnable runnable = null;
    public MyTask3(Runnable runnable, long delay) {
        this.runnable = runnable;
        //需要执行的时刻
        this.time = System.currentTimeMillis() + delay;
    }
    //获取时间
    public long getTime() {
        return time;
    }
    //获取需要执行的代码
    public void run() {
        runnable.run();
    }
    
}

实现MyTask的比较

因为Mytask是描述任务,而这些任务需要放到优先级队列中比较,就需要实现Comparable或者比较器.这里我们实现Comparable接口.

class MyTask3 implements Comparable<MyTask3>{
    //
    private long time;
    private Runnable runnable = null;
    public MyTask3(Runnable runnable, long delay) {
        this.runnable = runnable;
        //需要执行的时刻
        this.time = System.currentTimeMillis() + delay;
    }
    //获取时间
    public long getTime() {
        return time;
    }
    //获取需要执行的代码
    public void run() {
        runnable.run();
    }
    //比较时间快慢方法
    @Override
    public int compareTo(MyTask3 o) {
        return (int)(this.time - o.time);
    }
}

MyTimer类的构架

这里最核心的就是priorityQueue这个优先级队列,用它来存放我们的任务. 所对象为我们后面起到一个加锁的作用.

class MyTimer3 {
    //用优先级队列来存放任务
    private PriorityQueue<MyTask3> priorityQueue = new PriorityQueue<>();
    //锁对象
    private Object blocker = new Object();
    public void schedule(Runnable runnable, long time) {
        //核心方法
    }
}

schedule方法

这里我们在核心方法schedule中创建出一个任务,再将这个任务放入优先级队列中.

public void schedule(Runnable runnable, long time) {
        //创建一个任务
        MyTask3 myTask3 = new MyTask3(runnable, time);
        //将创建的任务放入优先级队列中
        priorityQueue.offer(myTask3);
    }

内置线程

这里将内置线程放入构造方法中,当这个类一创建就开始执行. 这里通过while循环来不断扫描.

//内置线程
    public MyTimer3() {
        //创建一个线程
        Thread t = new Thread(() -> {
            //通过while来不断扫描
            while(true) {
                //判断优先级队列是不是空的
                if(priorityQueue.isEmpty()) {
                    //为空就等待
                    //continue;
                }
                //当优先级队列中有任务时,取出任务
                MyTask3 myTask3 = priorityQueue.peek();
                //当前时间
                long time = System.currentTimeMillis();
                //如果到时间了就执行
                if(time >= myTask3.getTime()) {
                    myTask3.run();
                    priorityQueue.take();
                }else {
                    //时间没到等待
                    //continue;
                }
                
            }
        });
        t.start();
    }

线程安全与wait等待

这里发现schedule方法和构造方法都会有对于priorityQueue优先级队列的读和修改,这里可能就会出现线程安全问题,我们就需要为他们加上锁. 为空时我们就通过wait方法等待,等调用schedule方法使用notify唤醒它. 当有元素时但时间没到也是使用wait有时间的等待,时间到了就解除等待.

public void schedule(Runnable runnable, long time) {
        synchronized (blocker) {
            //创建一个任务
            MyTask3 myTask3 = new MyTask3(runnable, time);
            //将创建的任务放入优先级队列中
            priorityQueue.offer(myTask3);
            //通过notify来唤醒扫描线程
            blocker.notify();
        }
    }

    //内置线程
    public MyTimer3() {
        //创建一个线程
        Thread t = new Thread(() -> {
            //通过while来不断扫描
            while(true) {
                //加锁
                synchronized (blocker) {
                    try {
                        //判断优先级队列是不是空的
                        if(priorityQueue.isEmpty()) {
                            //为空就等待
                            blocker.wait();
                        }
                        //当优先级队列中有任务时,取出任务
                        MyTask3 myTask3 = priorityQueue.peek();
                        //当前时间
                        long time = System.currentTimeMillis();
                        //如果到时间了就执行
                        if(time >= myTask3.getTime()) {
                            myTask3.run();
                            priorityQueue.poll();
                        }else {
                            //时间没到等待 通过wait等待 有时间的等待.
                            blocker.wait(myTask3.getTime() - time);
                        }
                    }catch(InterruptedException o) {
                        o.printStackTrace();
                    }
                }
            }
        });
        //启动线程
        t.start();
    }

具体代码

class MyTask3 implements Comparable<MyTask3>{
    //
    private long time;
    private Runnable runnable = null;
    public MyTask3(Runnable runnable, long delay) {
        this.runnable = runnable;
        //需要执行的时刻
        this.time = System.currentTimeMillis() + delay;
    }
    //获取时间
    public long getTime() {
        return time;
    }
    //获取需要执行的代码
    public void run() {
        runnable.run();
    }
    //比较时间快慢方法
    @Override
    public int compareTo(MyTask3 o) {
        return (int)(this.time - o.time);
    }
}

class MyTimer3 {
    //用优先级队列来存放任务
    private PriorityQueue<MyTask3> priorityQueue = new PriorityQueue<>();
    //所对象
    private Object blocker = new Object();
    //核心方法
    public void schedule(Runnable runnable, long time) {
        synchronized (blocker) {
            //创建一个任务
            MyTask3 myTask3 = new MyTask3(runnable, time);
            //将创建的任务放入优先级队列中
            priorityQueue.offer(myTask3);
            //通过notify来唤醒扫描线程
            blocker.notify();
        }
    }

    //内置线程
    public MyTimer3() {
        //创建一个线程
        Thread t = new Thread(() -> {
            //通过while来不断扫描
            while(true) {
                //加锁
                synchronized (blocker) {
                    try {
                        //判断优先级队列是不是空的
                        if(priorityQueue.isEmpty()) {
                            //为空就等待
                            blocker.wait();
                        }
                        //当优先级队列中有任务时,取出任务
                        MyTask3 myTask3 = priorityQueue.peek();
                        //当前时间
                        long time = System.currentTimeMillis();
                        //如果到时间了就执行
                        if(time >= myTask3.getTime()) {
                            myTask3.run();
                            priorityQueue.poll();
                        }else {
                            //时间没到等待 通过wait等待 有时间的等待.
                            blocker.wait(myTask3.getTime() - time);
                        }
                    }catch(InterruptedException o) {
                        o.printStackTrace();
                    }
                }
            }
        });
        //启动线程
        t.start();
    }
}

代码执行流程


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

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

相关文章

漏洞复现-海康威视 NCG 联网网关 login.php 目录遍历漏漏洞(附漏洞检测脚本)

免责声明 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直接或者间接的…

新增数据,某个字段的值总是保存不上问题解决

在系统中新增一条数据&#xff0c;某个字段的数据总是保存不上&#xff0c;但是没有报任何异常和错误&#xff0c;其他字段也都是正常的&#xff0c;通过抓包分析请求参数发现那个字段的值也没有传给后端&#xff0c;检查了前后端代码也没有排查到问题。百思不得其解&#xff0…

【5G PHY】5G小区类型、小区组和小区节点的概念介绍

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

深入理解——快速排序

目录 &#x1f4a1;基本思想 &#x1f4a1;基本框架 &#x1f4a1;分割方法 ⭐Hoare版本 ⭐挖坑法 ⭐前后指针法 &#x1f4a1;优化方法 ⭐三数取中法 ⭐小区间内使用插入排序 &#x1f4a1;非递归实现快速排序 &#x1f4a1;性能分析 &#x1f4a1;基本思想 任取待排…

Java已死!

许多开发者仍然认为 Java 与当今时代息息相关&#xff0c;看完本文&#xff0c;你会发现 Java 的影响力已经大幅减弱。实际上&#xff0c;Java 是一种濒临灭绝的编程语言。尽管 Java 一直是世界上使用最广泛、最受欢迎的编程语言之一&#xff0c;但它很快就会面临消亡的危险。 …

【OpenHarmony 北向应用开发】ArkTS语言入门(构建应用页面)

ArkTS语言入门 在学习ArkTS语言之前&#xff0c;我们首先需要一个能够编译并运行该语言的工具 DevEco Studio。 了解ArkTS ArkTS是OpenHarmony优选的主力应用开发语言。ArkTS围绕应用开发在TypeScript&#xff08;简称TS&#xff09;生态基础上做了进一步扩展&#xff0c;继…

01--二分查找

一. 初识算法 1.1 什么是算法&#xff1f; 在数学和计算机科学领域&#xff0c;算法是一系列有限的严谨指令&#xff0c;通常用于解决一类特定问题或执行计算 不正式的说&#xff0c;算法就是任何定义优良的计算过程&#xff1a;接收一些值作为输入&#xff0c;在有限的时间…

【动态读取配置文件】ParameterTool读取带环境的配置信息

不同环境Flink配置信息是不同的&#xff0c;为了区分不同环境的配置文件&#xff0c;使用ParameterTool工具读取带有环境的配置文件信息 区分环境的配置文件 三个配置文件&#xff1a; flink.properties&#xff1a;决定那个配置文件生效 flink-dev.properties&#xff1a;测…

【计算机网络】—— 详解码元,传输速率的计算|网络奇缘系列|计算机网络

&#x1f308;个人主页: Aileen_0v0&#x1f525;系列专栏: 一见倾心,再见倾城 --- 计算机网络~&#x1f4ab;个人格言:"没有罗马,那就自己创造罗马~" 目录 码元 速率和波特 思考1 思考2 思考3 带宽&#xff08;Bandwidth&#xff09; &#x1f4dd;总结 码元…

MapReduce序列化实例代码

1 &#xff09;需求&#xff1a;统计每个学号该月的超市消费、食堂消费、总消费 2 &#xff09;输入数据格式 序号 学号 超市消费 食堂消费 18 202200153105 8.78 12 3 &#xff09;期望输出格式 key &#xff08;学号&#xff09; value &#xff08; bean 对象&#xf…

用Rust刷LeetCode之58 最后一个单词的长度

58. 最后一个单词的长度[1] 难度: 简单 原描述: 思路 使用标准库: package mainimport ( "fmt" "strings")func lengthOfLastWord(s string) int { s strings.TrimSpace(s) // 删除 首尾 的空格 arr : strings.Split(s, " ") // 字符串转为切片…

FL Studio 21.1.0.3713中文版最新安装激活图文教程及系统配置要求

FL Studio 21.1.0.3713中文版是一款功能强大的编曲软件&#xff0c;它也能够剪辑、混音、录音&#xff0c;它的矢量界面&#xff0c;能更好用在4K、5K甚至8K显示器上。完全重新设计混音器、动态缩放、具有 6 种布局风格、外加 3个用户自定义面板管理音轨、多推子选择和调整、混…

Python 反编译Il2Cpp APK

引入 https://github.com/Perfare/Il2CppDumper/ 实现 开源的Ii2Cpp Dumper可以帮助我们将So和globalmetadata.dat文件反编译出 Assembly-CSharp.dll 本博客教程可以帮助我们直接拖入APK反编译出来 调用方式 两种 第一种 拖入后回车运行 第二种 放入运行的根目录下 源码 i…

Pandas-DataFtame的索引与切片(第3讲)

Pandas-DataFtame的索引与切片(第3讲)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ�…

数据解密战:.mallox勒索病毒攻击下的数据保护

引言&#xff1a; 近期&#xff0c;网络安全领域再度掀起一场不安的浪潮&#xff0c;.Mallox勒索病毒作为最新一位悍匪登场&#xff0c;以其毒辣的加密技术&#xff0c;将用户的数据转瞬间变成了无解之谜。这股数字黑暗力量的出现引起了广泛关注&#xff0c;让用户和企业重新审…

事件监听的艺术:掌握`addEventListener`的魅力

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

前端实现一个时间区间内,再次单选功能,使用Antd组件库内日历组件Calendar

需求&#xff1a;需要先让用户选择一个时间区间&#xff0c;然后再这个时间区间中&#xff0c;让用户再次去单选其种特殊日期。 思路&#xff1a; 1.先用Antd组件库中日期选择DatePicker.RangePicker实现让用户选择时间区间 2.在选择完时间区间后&#xff0c;用这个时间区间…

Java之Clonable接口和深浅拷贝

Clonable接口 我们船舰了一个人的对象&#xff0c;想要克隆一个一模一样的对象&#xff0c;可以用到object类里面的克隆方法 object不是所有类的父类吗&#xff1f;那为什么用person1点不出这个方法呢&#xff1f;可以看一下源码 这是Object类里面的clone方法的声明&#xff0…

随机游走Python中的实现

随机游走是一个数学对象&#xff0c;称为随机或随机过程&#xff0c;它描述了一条路径&#xff0c;该路径由一些数学空间&#xff08;如整数&#xff09;上的一系列随机步骤组成。随机游走的一个基本例子是整数线上的随机游走&#xff0c;它从0开始&#xff0c;每一步以相等的概…

docker小白第五天

docker小白第五天 docker的私有库 有些涉密的信息代码不能放在阿里云的镜像仓库&#xff0c;因此需要构建一个个人内网专属的私有库&#xff0c;将镜像或者容器代码进行推送保存。 下载镜像docker registry 执行代码docker pull registry&#xff0c;用于搭建私服前的准备。…