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步骤&#…

关于学习和技术提升的思考

从技术框架的演进可以看出专业性越来越强&#xff0c;门槛越来越低&#xff0c;我们经常强调要解耦合&#xff0c;把公共模块功能做抽取&#xff0c;减少重复劳动&#xff0c;使主要精力专注于特定场景特定业务的处理。老美在设计框架的时候也是这么干的。殊不知&#xff0c;我…

微服务-4 Nacos

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

2024年华为OD机试真题-符号运算-Python-OD统一考试(C卷)

题目描述: 给定一个表达式,求其分数计算结果 表达式的限制如下: 1. 所有的输入数字皆为正整数(包括0) 2. 仅支持四则运算(+-*/)和括号 3. 结果为整数或分数, 分数必须化为最简格式(比如6, 3/4, 7/8, 90/7) 4. 除数可能为0,如果遇到这种情况,直接输出"ERROR" 5.…

[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 …

【基础】损失函数和评估指标的区别

文章目录 一.损失函数&#xff08;Loss Function&#xff09;&#xff1a;1.1 作用1.2 示例1.3 常见面试题1.3.1 常见的损失函数有哪些&#xff1f;它们分别适用于什么类型的任务&#xff1f;1.3.2 介绍一下交叉熵损失函数&#xff1f;1.3.3 均方误差损失函数&#xff08;Mean …

如何使用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 做的精彩贡献&…

[Java、Android面试]_17_Fragment和Service生命周期

本人今年参加了很多面试&#xff0c;也有幸拿到了一些大厂的offer&#xff0c;整理了众多面试资料&#xff0c;后续还会分享众多面试资料。 整理成了面试系列&#xff0c;由于时间有限&#xff0c;每天整理一点&#xff0c;后续会陆续分享出来&#xff0c;感兴趣的朋友可关注收…

将数组元素转化为指针的方法

1. 数组名作为指针 int arr[5] {0,1,2,3,4}; int*ptr arr; 2. 取地址 int* ptr &arr[3]; 3.指针算数运算 int* ptr arr; int* p *(arr1);

【个人使用分享】教你大厂云服务器如何选 联机不卡顿 幻兽帕鲁小白一键部署 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;并…

Java 中装饰模式,请用代码具体举例

装饰器模式允许你通过将对象放入包含行为的特殊包装对象中来为原对象动态添加新的行为。以下是一个简单的 Java 示例&#xff1a; // 定义接口 interface Component {void operation(); }// 具体组件实现接口 class ConcreteComponent implements Component {public void opera…

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

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

python 会员信息管理系统2.0

问题介绍 综合案例实现&#xff1a;会员管理系统设计与实现-V3 利用所学习的知识点 ,结合会员管理系统的分析与实现&#xff0c; 了解面向对象开发过程中类内部功能的分析方法&#xff0c;系统讲解 Python语法、控制结构、四种典型序列 ,函数定义以及面向对象语法和模块的应用…