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