JavaEE 初阶篇-深入了解定时器、工厂模式和比较器

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍

文章目录

        1.0 定时器概述

        2.0 实现定时器

        2.1 实现定时器 - 定义 MyTask 任务类

        2.2 实现定时器 - MyTimer 定时器类存放任务的方法

        2.3 实现定时器 - MyTimer 定时器类读取任务的方法

        2.4 实现定时器的完整代码

        3.0 简单工厂模式概述

        4.0 深入了解比较器(Comparable 与 Comparator 区别)

        4.1 Comparable 接口

        4.2 Comparator 接口


        1.0 定时器概述

        定时器(Timer)是 Java 中用于执行定时任务的工具类,可以在指定的时间点执行某个任务,也可以按照一定的时间间隔重复执行任务。定时器提供了一种简单而有效的方式来安排任务的执行。

        2.0 实现定时器

        2.1 实现定时器 - 定义 MyTask 任务类

        定义一个任务类 MyTask ,成员变量有 Runnable 类型 runnable 变量long 类型的 time 变量。利用构造方法获取值即可。

        其中 this.time = time + System.currentTimeMillis() 换算成具体什么时间执行。

        该 Task 类需要实现 Comparable 接口重写 compareTo() 方法。是因为一旦有多个任务生成时,且将任务放入到容器中,比如说数组容器中,这就会导致需要不断的扫描数组中的任务,还有多长时间该执行了。为了解决这一弊端,直接把任务放入到优先级队列(小顶堆)中来,直接查看堆顶元素,因为堆顶元素剩余时间是最小的,如果堆顶元素还没到时间去执行任务,那么剩余的任务也一定还没到时间开始执行。最终,需要任务类 Task 需要实现内部比较器接口。

代码如下:

public class MyTask implements Comparable<MyTask>{public Runnable runnable;public long time;public MyTask(Runnable runnable,long time){this.runnable = runnable;this.time = time + System.currentTimeMillis();}@Overridepublic int compareTo(MyTask o) {return (int) (this.time - o.time);}}

        2.2 实现定时器 - MyTimer 定时器类存放任务的方法

        定义一个 MyTimer 定时器类,先要考虑的是任务需要存放在什么样的容器中,上面说到了需要用到优先级队列 PriorityQueue 数据结构。

    private final PriorityQueue<MyTask> queue = new PriorityQueue<>();

        MyTimer 类中的实例方法,再来实现将任务放入到容器。直接用 queue.offer() 方法,将任务对象放进 queue 容器中即可。不过需要考虑线程安全问题,当把任务放入到队列中这一个过程中,不允许有其他线程来读取任务,因此需要加锁处理。当任务已经传进队列完毕后,唤醒其他线程开始执行读取任务这个操作。

    public void schedule(Runnable runnable,long time){synchronized (queue){MyTask myTask = new MyTask(runnable,time);queue.offer(myTask);queue.notify();}}

        2.3 实现定时器 - MyTimer 定时器类读取任务的方法

        在 MyTime 实例对象创建之后,就可以尝试去读取队列中的任务了,因此在构造器中创建一个线程来读取队列中的任务

        当队列中的任务为空时,需要阻塞等待 wait() 方法,释放锁,再让当前线程进入阻塞等待。直到任务进来了,等到其他线程唤醒之后,当前线程才会去竞争锁,一旦竞争到锁之后,再判断队列中是否为空,此时不为空了,再从队列中查看堆顶任务,不能直接 poll() ,虽然队列中有任务了,但是时间到了吗?此时需要进一步验证,用当前的时间跟 queue.peek() 中的任务中的时间进行比较,一旦当前任务大于 queue.peek() 中的任务时间,就可以执行 runnable.run() 方法了,当然这个方法不需要我们手动去调用,这是一个回调函数,系统会自动调用的。

        还有一种情况:队列中不为空,且堆顶任务时间还没到,就需要释放锁之后继续阻塞等待 wait() 。但是这里需要死等吗?

        当然不能死等,万一没有任务进来了,没有人唤醒当前线程怎么办?所以不能一直死等,可以设置时间,时间一到,就可以重新竞争获取锁。

        但是对于队列为空这种情况就需要死等了,没有任务只能死等了。

        还有一个要注意的地方,可以换成 sleep() 方法等待吗?答案是不可以的,因为 sleep() 方法在等待的时候,是不会释放锁的,这就会导致万一有新任务进来的时候,锁一直被当前线程占用着。用 wait() 方法,可以提前释放锁等待,不会出现新任务进来线程一直被占用着的这种情况发生。还会提前唤醒当前线程去读取堆顶任务,万一顶堆任务更新了,就可以立马执行到位了。

        最后不要忘记进行线程启动。

代码如下:

    public MyTimer(){Thread thread = new Thread(()->{while (true){synchronized (queue){if (queue.isEmpty()){try {queue.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}MyTask myTask = queue.peek();long curTime = System.currentTimeMillis();if (myTask.time <= curTime){//时间到了,考可以执行任务了myTask.runnable.run();queue.poll();}else {try {queue.wait(myTask.time - curTime);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}});thread.start();}

        2.4 实现定时器的完整代码

代码如下:

main 方法:

        定义了三个任务,分别定时为 1s 、20s 、3s

public class demo1 {public static void main(String[] args) {MyTimer myTimer = new MyTimer();myTimer.schedule(()->{System.out.println(Thread.currentThread().getName() + "--> 1s 之后输出");},1000);myTimer.schedule(()->{System.out.println(Thread.currentThread().getName() + "--> 20s 之后输出");},20000);myTimer.schedule(()->{System.out.println(Thread.currentThread().getName() + "--> 3s 之后输出");},3000);}
}

MyTask 类:

public class MyTask implements Comparable<MyTask>{public Runnable runnable;public long time;public MyTask(Runnable runnable,long time){this.runnable = runnable;this.time = time + System.currentTimeMillis();}@Overridepublic int compareTo(MyTask o) {return (int) (this.time - o.time);}}

MyTimer 类:

import java.util.PriorityQueue;public class MyTimer {private final PriorityQueue<MyTask> queue = new PriorityQueue<>();public void schedule(Runnable runnable,long time){synchronized (queue){MyTask myTask = new MyTask(runnable,time);queue.offer(myTask);queue.notify();}}public MyTimer(){Thread thread = new Thread(()->{while (true){synchronized (queue){if (queue.isEmpty()){try {queue.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}MyTask myTask = queue.peek();long curTime = System.currentTimeMillis();if (myTask.time <= curTime){//时间到了,考可以执行任务了myTask.runnable.run();queue.poll();}else {try {queue.wait(myTask.time - curTime);} catch (InterruptedException e) {throw new RuntimeException(e);}}}}});thread.start();}
}

运行结果:

        3.0 简单工厂模式概述

        工厂模式可以解决当设计构造方法中,参数列表一样,但是具体实现的逻辑不一样这种情况。这就需要用到工厂模式了。因为不能对构造方法进行重载,由于参数列表是完全一样,且构造方法的名字也是不能改变的。

        这就是遇到了参数列表相同,但是执行的逻辑不一样情况。 

        就可以通过静态方法来创建实例对象。

        在工厂模式中,可以通过不同的工厂方法或者不同的工厂类来创建对象,从而实现根据不同条件返回不同的实现对象。这种方式避免了构造方法重载的限制,让代码更加灵活和可扩展。

        通过一个工厂类来创建对象,客户端只需要传入对应的参数即可获得所需的对象。这种方式不符合传统意义上的工厂模式,但是仍然是一种创建对象的方式。 

举个例子:

public class Factory {private int a;private int b;public void setA(int a) {this.a = a;}public void setB(int b) {this.b = b;}public static Factory abFun(int a, int b){Factory factory = new Factory();factory.setA(a);factory.setB(b);return factory;}public static Factory baFun(int a,int b){Factory factory = new Factory();factory.setA(b);factory.setB(a);return factory;}public void print(){System.out.println(a - b);}}

        4.0 深入了解比较器(Comparable 与 Comparator 区别)

        在Java中,有两种常见的比较对象的方式。

        4.1 Comparable 接口

        属于内部比较器 ,Comparable 接口是在对象自身的类中实现的,用于指定对象之间的自然顺序。一个类实现了 Comparable 接口后,就可以通过调用 compareTo() 方法来比较对象的顺序。只能对该类的对象进行排序,不能对其他类的对象进行排序。 

代码如下:

public class Person implements Comparable<Person> {private String name;private int age;// 其他属性和方法@Overridepublic int compareTo(Person otherPerson) {return this.age - otherPerson.age;}
}

        4.2 Comparator 接口

        属于外部比较器,是一个独立的比较器,可以用于对不同类的对象进行排序,而且可以定义多种不同的排序规则。通过实现 Comparator 接口并重写 compare() 方法,可以指定不同的比较规则。 

代码如下:

public class PersonComparator implements Comparator<Person> {@Overridepublic int compare(Person p1, Person p2) {return p1.compareTo(p2);}
}

        这个外部比较器就比较灵活,没有固定写死。对于在类本身实现 Comparable 接口已经固定写死的。

        例如在使用 Collections.sort()方法对集合进行排序时可以传入自定义的比较器来指定排序规则。Collections.sort() 如果没有传入比较器,默认会用类本身实现的 comparable 接口中的comparaTo 方法。

 

// 创建一个比较器对象
Comparator<Person> personComparator = new Comparator<Person>() {@Overridepublic int compare(Person p1, Person p2) {// 自定义比较规则return Integer.compare(p1.getAge(), p2.getAge());}
};// 创建一个包含Person对象的列表
List<Person> personList = new ArrayList<>();
personList.add(new Person("Alice", 25));
personList.add(new Person("Bob", 30));
personList.add(new Person("Charlie", 20));// 使用Collections.sort()方法并传入比较器
Collections.sort(personList, personComparator);// 打印排序后的列表
for (Person person : personList) {System.out.println(person.getName() + ": " + person.getAge());
}

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

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

相关文章

前端开发攻略---根据音频节奏实时绘制不断变化的波形图。深入剖析如何通过代码实现音频数据的可视化。

1、演示 2、代码分析 逐行解析 JavaScript 代码块&#xff1a; const audioEle document.querySelector(audio) const cvs document.querySelector(canvas) const ctx cvs.getContext(2d)这几行代码首先获取了 <audio> 和 <canvas> 元素的引用&#xff0c;并使用…

群晖虚拟机搭建Synology Drive并实现Obsidian笔记异地多端同步

文章目录 一、简介软件特色演示&#xff1a; 二、使用免费群晖虚拟机搭建群晖Synology Drive服务&#xff0c;实现局域网同步1 安装并设置Synology Drive套件2 局域网内同步文件测试 三、内网穿透群晖Synology Drive&#xff0c;实现异地多端同步Windows 安装 Cpolar步骤&#…

微服务-4 Nacos

目录 一、注册中心 二、配置管理 1. 添加配置 2. 配置自动刷新 3. 多环境配置共享​编辑 一、注册中心 服务列表&#xff1a; 服务详情&#xff1a; 二、配置管理 1. 添加配置 (1). 在 nacos 界面中添加配置文件&#xff1a; 配置列表&#xff1a; 配置详情&#xff1a;…

[C++][算法基础]模拟散列表(哈希表)

维护一个集合&#xff0c;支持如下几种操作&#xff1a; I x&#xff0c;插入一个整数 x&#xff1b;Q x&#xff0c;询问整数 x 是否在集合中出现过&#xff1b; 现在要进行 N 次操作&#xff0c;对于每个询问操作输出对应的结果。 输入格式 第一行包含整数 N&#xff0c;…

致远目前最新V8.2,打开公文正文的原文档,提示错误:http返回码=12157……解决办法

新版V8.2&#xff0c;打开发文中的正文原文档时&#xff0c;提示错误&#xff1a;http返回码12157&#xff0c;返回信息安全频道支持出错&#xff0c;更新安装插件也不能解决。 解决办法 设置IE浏览器的Internt选项配置。 控制面板-网络和Internet–Internet选项-高级-使用TLS …

如何使用Tomcat+cpolar搭建本地站点并发布到公网分享好友远程访问

文章目录 前言1.本地Tomcat网页搭建1.1 Tomcat安装1.2 配置环境变量1.3 环境配置1.4 Tomcat运行测试1.5 Cpolar安装和注册 2.本地网页发布2.1.Cpolar云端设置2.2 Cpolar本地设置 3.公网访问测试4.结语 前言 Tomcat作为一个轻量级的服务器拥有强大功能&#xff0c;由于其可以实…

【React】React Hooks

useState useState 向组件中添加状态变量 状态是只读的&#xff0c;不可以直接修改 对于对象类型的状态变量&#xff0c;应该传递一个新的对象来更改 需要对象展开&#xff0c;并重新赋值&#xff0c;进行增加或者修改。 如果需要删除&#xff0c;则使用 filter。 import { …

【踩坑日记】Pop!OS中文输入法的坑

文章目录 前言一、编译安装最新的IBus-pinyin输入法1.卸载旧输入法2.安装编译依赖3.下载源码4.编译和安装libpinyin5.编译和安装ibus-libpinyin6.重启IBus服务二、安装Fcitx5前言 使用Linux时,特别是涉及到中文的时候,会遇到一些问题。我最近在使用Pop!OS 22.04,这是Ubuntu…

Qt for MCUs 2.7正式发布

本文翻译自&#xff1a;Qt for MCUs 2.7 released 原文作者&#xff1a;Qt Group高级产品经理Yoann Lopes 翻译&#xff1a;Macsen Wang Qt for MCUs的新版本已发布&#xff0c;为Qt Quick Ultralite引擎带来了新功能&#xff0c;增加了更多MCU平台的支持&#xff0c;并且我们…

Apache SeaTunnel 社区 3 月月报

各位热爱 SeaTunnel 的小伙伴们&#xff0c;SeaTunnel 社区 3 月月报来啦&#xff01;这里将记录 SeaTunnel 社区每个月的重要更新&#xff0c;并评选出月度之星&#xff0c;欢迎关注。 SeaTunnel 月度 Merge Stars 感谢以下小伙伴 3 月为 Apache SeaTunnel 做的精彩贡献&…

【个人使用分享】教你大厂云服务器如何选 联机不卡顿 幻兽帕鲁小白一键部署 16G低至26 续费1.5折

更新日期&#xff1a;4月11日&#xff08;半年档 价格回调&#xff0c;京东云采购季持续进行&#xff09; 本文纯原创&#xff0c;侵权必究 《最新对比表》已更新在文章头部—腾讯云文档&#xff0c;文章具有时效性&#xff0c;请以腾讯文档为准&#xff01; 【腾讯文档实时更…

计算机网络——WEB服务器编程实验

实验目的 1. 处理一个 http 请求 2. 接收并解析 http 请求 3. 从服务器文件系统中获得被请求的文件 4. 创建一个包括被请求的文件的 http 响应信息 5. 直接发送该信息到客户端 具体内容 一、C 程序来实现 web 服务器功能。 二、用 HTML 语言编写两个 HTML文件&#xff0c;并…

面向对象设计原则实验“依赖倒置原则”

高层模块不应该依赖于低层模块。二者都应该依赖于抽象。抽象不应该依赖于细节。细节应该依赖于抽象。 &#xff08;开闭原则、里氏代换原则和依赖倒转原则的三个实例很相似&#xff0c;原因是它之间的关系很紧密&#xff0c;在实现很多重构时通常需要同时使用这三个原则。开闭…

【RealSense】Ubuntu20.04 安装 Intel RealSense ROS 并使用 D435i 测试

【RealSense】Ubuntu20.04 安装 Intel RealSense ROS 并使用 D435i 测试 1 本机环境2 安装流程3 存在的 bug3.1 Resource not found: rgbd_launch 1 本机环境 Ubuntu20.04ROS Noetic 2 安装流程 参考文档: Link 安装 Intel RealSense™ SDK 2.0&#xff0c;参考上一篇文章: L…

『研学倒计时』日本《极致产品力》顾问式研学再出发!

信心行动》共建中国食品行业风向标 2024年,信心比黄金还重要! 深度学习日本就是最佳路线。过去五年,许多中国企业开始研究与借鉴日本,元气森林、东方树叶、奈雪的茶、RIO鸡尾酒、蒟蒻果冻、丸美美妆、花印、名创优品.….从日本“失去的20年”中吸收逆势增长经验。 一、研学背…

快速列表quicklist

目录 为什么使用快速列表quicklist 对比双向链表 对比压缩列表ziplist quicklist结构 节点结构quicklistNode quicklist 管理ziplist信息的结构quicklistEntry 迭代器结构quicklistIter quicklist的API 1.创建快速列表 2.创建快速列表节点 3.头插quicklistPushHead …

【Python】控制台进度条

在Python开发中&#xff0c;有时需要向用户展示一个任务的进度&#xff0c;以提供更好的交互体验。下面我将展示如何使用Python来创建一个简单的控制台进度条。 效果&#xff1a; 代码&#xff1a; import time import sys def print_progress_bar(completed, total, length…

STM32 定时器外部时钟与循迹模块

1、上篇文章介绍了定时器使用内部时钟信号计数&#xff0c;实现计数、更新中断等功能。 2、可不可以利用定时器对来自外部的信号进行计数&#xff1f;也就是对输入io的信号计数&#xff1f; 3、可以 1、定时器外部时钟 STM32的通用定时器和高级定时器都将外部信号引入…

【Algorithms 4】算法(第4版)学习笔记 24 - 5.5 数据压缩

文章目录 前言参考目录学习笔记1&#xff1a;介绍2&#xff1a;游程编码 run-length encoding2.1&#xff1a;介绍2.2&#xff1a;Java 实现3&#xff1a;霍夫曼压缩 Huffman compression3.1&#xff1a;变长前缀码 variable-length codes3.1.1&#xff1a;介绍3.1.2&#xff1…

【IC前端虚拟项目】验证环境方案思路和文档组织

【IC前端虚拟项目】数据搬运指令处理模块前端实现虚拟项目说明-CSDN博客 对于mvu的验证环境,从功能角度就可以分析出需要搭建哪些部分,再看一下mvu的周围环境哈: 很明显验证环境必然要包括几个部分: 1.模拟idu发送指令; 2.模拟ram/ddr读写数据; 3.rm模拟mvu的行为; …