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

news2025/2/25 2:11:05

🔥博客主页: 【小扳_-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();
    }

    @Override
    public 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();
    }

    @Override
    public 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;

    // 其他属性和方法

    @Override
    public int compareTo(Person otherPerson) {
        return this.age - otherPerson.age;
    }
}

        4.2 Comparator 接口

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

代码如下:

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

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

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

 

// 创建一个比较器对象
Comparator<Person> personComparator = new Comparator<Person>() {
    @Override
    public 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.coloradmin.cn/o/1585820.html

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

相关文章

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

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

二叉树应用——最优二叉树(Huffman树)、贪心算法—— Huffman编码

1、外部带权外部路径长度、Huffman树 从图中可以看出&#xff0c;深度越浅的叶子结点权重越大&#xff0c;深度越深的叶子结点权重越小的话&#xff0c;得出的带权外部路径长度越小。 Huffman树就是使得外部带权路径最小的二叉树 2、如何构造Huffman树 &#xff08;1&#xf…

InternlM2

第一次作业 基础作业 进阶作业 1. hugging face下载 2. 部署 首先&#xff0c;从github上git clone仓库 https://github.com/InternLM/InternLM-XComposer.git然后里面的指引安装环境

鸿蒙4.2开放升级,首批公测包含24款机型,快来升级尝鲜

在今天下午刚刚结束的鸿蒙生态春季沟通会上&#xff0c;余承东提到了一个关于鸿蒙系统的细节&#xff0c;那就是目前top5000的App里&#xff0c;已经有4000多款App确定加入。 如此量级&#xff0c;预测基本能够覆盖常用的应用了&#xff0c;此前担心的应用生态问题也得到解决&a…

Harmony鸿蒙南向驱动开发-I3C接口使用

功能简介 I3C&#xff08;Improved Inter Integrated Circuit&#xff09;总线是由MIPI Alliance开发的一种简单、低成本的双向二线制同步串行总线。 I3C是两线双向串行总线&#xff0c;针对多个传感器从设备进行了优化&#xff0c;并且一次只能由一个I3C主设备控制。相比于I…

[计算机效率] 鼠标手势工具:WGestures(解放键盘的超级效率工具)

3.22 鼠标手势工具&#xff1a;WGestures 通过设置各种鼠标手势和操作进行绑定。当用户通过鼠标绘制出特定的鼠标手势后就会触发已经设置好的操作。有点像浏览器中的鼠标手势&#xff0c;通过鼠标手势操纵浏览器做一些特定的动作。这是一款强大的鼠标手势工具&#xff0c;可以…

Unity 布局 HorizontalLayoutGroup 多行 换行

演示Gif&#xff1a; 现象: 子元素宽度不同&#xff0c;超出父元素后不会换行 GridLayout则是固定宽度也不能用&#xff0c; 需求 水平排版的同时&#xff0c;超出父级后换行 代码&#xff1a; 催更就展示[狗头]

李廉洋;4.11#黄金,WTI原油#行情走势分析策略。

美国银行预计&#xff0c;在今天召开的欧洲央行会议上不会有重大的政策变化&#xff0c;但欧洲央行正逐渐接近开始降息&#xff0c;尽管它采取的是一种谨慎的、依赖数据的方式。虽然欧洲央行对降息轨迹的信心不断增强&#xff0c;但降息的具体速度和幅度仍未公布&#xff0c;而…

第06章 网络传输介质

6.1 本章目标 了解双绞线分类和特性了解同轴电缆分类和特性了解光纤分类和特性了解无线传输介质分类和特性 6.2 传输介质分类 现在社会还是以有线介质为主 计算机通信 - 有线通信 - 无线通信有线通信传输介质 - 双绞线 - 同轴电缆 - 光导纤维无线通信 - 卫星 - 微波 - 红外…

SpringBoot 整合RocketMQ

目录 一、引入依赖 二、配置文件 三、生产者 四、消费者 五、结果 一、引入依赖 <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>2.2.0</version> </d…

Linux网络名称空间的调试方法全面分析

Linux网络名称空间是一种广泛使用的技术&#xff0c;用于隔离网络环境&#xff0c;特别是在容器化和微服务架构中&#x1f4e6;。然而&#xff0c;随着网络名称空间的广泛应用&#xff0c;开发者和系统管理员可能会遇到需要调试网络名称空间配置和性能的情况&#x1f50d;。本文…

Splitpanes拆分窗格插件使用

项目开发中用到了拆分窗格(就是下面的效果&#xff0c;可以拆分网页&#xff0c;我们项目通常都是用左右两块拆分&#xff0c;可以通过拖动图标进行左右拖动)&#xff0c;于是就发现了一个很好用的插件&#xff1a;Splitpanes 官网地址&#xff1a;Splitpanes (antoniandre.git…

ONERugged车载平板电脑厂家丨工业车载电脑优势体现丨3年质保

作为现代社会中必不可少的出行工具&#xff0c;汽车不仅仅是代步工具&#xff0c;更是我们生活中的重要一部分。而在如此多功能的汽车内&#xff0c;一款高可靠性、适应不同行业应用的车载平板电脑成为了当下的热门选择。ONERugged车载平板电脑以其卓越的品质和强大的功能而备受…

FMix: Enhancing Mixed Sample Data Augmentation 论文阅读

1 Abstract 近年来&#xff0c;混合样本数据增强&#xff08;Mixed Sample Data Augmentation&#xff0c;MSDA&#xff09;受到了越来越多的关注&#xff0c;出现了许多成功的变体&#xff0c;例如MixUp和CutMix。通过研究VAE在原始数据和增强数据上学习到的函数之间的互信息…

【力扣】125.验证回文串

刷题&#xff0c;过了真的好有成就感&#xff01;&#xff01;&#xff01; 题解&#xff1a; 根据题目要求&#xff0c;我们需要处理一下几个问题&#xff1a; 将大写字母转变成小写对原来的字符串进行处理&#xff0c;只要字母和数字考虑只有一个和字符串为空的情况 1、将…

Nginx 基础应用实战 06 构建一个PHP的站点

Nginx 基础应用实战 06 构建一个PHP的站点 使用套件 lmnp https://oneinstack.com 安装完成后 搭建bbs https://www.discuz.net/ 搭建博客 https://wordpress.com https://cn.wordpress.org/ CMS系统 http://www.dedecms.com/ 构建Lua站点 Openresty Nginx Lua …

lomobok源码编译学习笔记(1)

lomobok学习笔记&#xff08;1&#xff09; 项目导入 lombok的github地址 GitHub - projectlombok/lombok: Very spicy additions to the Java programming language. 开发工具 idea不知道为啥&#xff0c;装上ant工具也不好用&#xff0c;eclipse默认自带有ant,不需要装。…

0 idea搭建springboot项目

1 2 3 4 5 配置文件 application.yaml server:servlet:context-path: /app #项目名controller //注入到spring容器 Controller public class HelloController {GetMapping("hello")ResponseBodypublic String hello(){return "Hello,SpringBoot";} }启…

多态——C++

这里写目录标题 衔接继承总结继承和组合白箱复用黑箱复用 多态的概念多态的定义以及实现虚函数重写的两个例外协变面试题析构函数的重写 finalvoerride重载隐藏(重定义)重写(覆盖)抽象类什么是抽象类&#xff1f; 实现继承和接口继承多态的原理虚函数表 那多态的调用是怎么实现…

我为什么选择当程序员

在当今这个数字化时代&#xff0c;程序员已经成为了一个非常受欢迎的职业选择。无论是出于对技术的热爱&#xff0c;还是因为看到了这个行业的广阔前景&#xff0c;越来越多的人选择加入程序员的行列&#xff0c;尤其是最近几年AI带动整体行业的发展。本文将深入探讨人们选择成…