本文主要给大家讲解多线程的一个重要案例 — 定时器.
关注收藏, 开始学习吧
定时器也是软件开发中的一个重要组件 类似于一个 “闹钟”. 达到一个设定的时间之后, 就执行某个指定好的代码.
我们来使用一下标准库中的定时器.
import java.util.Timer;
import java.util.TimerTask;
public class ThreadDemo20 {
public static void main(String[] args) {
Timer timer = new Timer();
// 给 timer 中注册的这个任务, 不是在调用 schedule 的线程中执行的.
// 而是通过 Timer 内部的线程, 来负责执行的.
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("hello 3");
}
}, 3000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("hello 2");
}
}, 2000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("hello 1");
}
}, 1000);
System.out.println("program start");
}
}
代码效果
可以看到, hello 1 2 3 按照时间设置依次执行. Timer 内部, 有着自己的线程. 为了保证随时可以处理新安排的任务, 这个线程会持续执行, 并且这个线程是前台线程, 不可以被打断.
接下来我们自己尝试来实现一个定时器
要想实现一个定时器, 我们需要先想一想定时器其中的主要逻辑. 我们需要用一个类 (TimerTask) 将一个任务给描述出来, 再用一个类 (Timer) 使用数据结构将多个任务给组织起来.
在实现定时器时, 我们有了以上的逻辑支撑, 还需要注意三个关键问题:
掌握以上几点后, 我们便可以开始动手实现一个定时器了.
import java.util.PriorityQueue;
class MyTimerTask implements Comparable<MyTimerTask> {
private Runnable runnable;
private long time;
public MyTimerTask (Runnable runnable, long delay) {
this.runnable = runnable;
this.time = System.currentTimeMillis() + delay;
}
public Runnable getRunnable() {
return runnable;
}
public long getTime() {
return time;
}
@Override
public int compareTo(MyTimerTask o) {
// return (int)(o.time - this.time);
return (int)(this.time - o.time);
}
}
class MyTimer {
// 使用优先级队列, 来保存上述的 N 个任务
private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
// 用来加锁的对象
private Object locker = new Object();
// 定时器的核心方法, 就是要把执行的任务添加到队列中.
public void schedule(Runnable runnable, long delay) {
synchronized (locker) {
MyTimerTask task = new MyTimerTask(runnable, delay);
queue.offer(task);
// 每次来新的任务, 都唤醒一下之前的扫描线程, 让扫描线程根据新的任务重新规划时间.
locker.notify();
}
}
// MyTimer 中还需要构造一个 "扫描线程", 一方面去负责监控队首元素是否到点了, 是否应该执行; 一方面当任务到点之后,
// 就要调用这里的 Runnable 的 Run 方法来完成任务
public MyTimer() {
// 扫描线程
Thread thread = new Thread(() -> {
while (true) {
try {
synchronized (locker) {
while (queue.isEmpty()) {
// 注意, 当前如果队列为空, 此时就不应该去取这里的元素.
// 此处使用 wait 等待更合适.
// 如果使用 continue, 就会使这个线程 while 循环运行的飞快,
// 也会陷入一个高频占用 cpu 的状态(忙等).
// continue;
locker.wait();
}
// 拿出目前需要最早执行的任务
MyTimerTask task = queue.peek();
long curTime = System.currentTimeMillis();
if (curTime >= task.getTime()) {
// 假设当前时间是 14:01, 任务时间是 14:00,
// 此时就意味着应该要执行这个任务了.
queue.poll();
task.getRunnable().run();
} else {
// 让当前扫描线程休眠一下, 按照时间差来进行休眠.
// Thread.sleep(task.getTime() - curTime);
locker.wait(task.getTime() - curTime);
}
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
thread.start();
}
}
✨ 本文讲解了多线程案例中的一个经典案例, 定时器, 带大家简单了解了一下标准库库中的定时器, 重点需要掌握其核心的逻辑是什么, 如何自己去实现一个定时器.
✨ 想了解更多的多线程知识, 可以收藏一下本人的多线程学习专栏, 里面会持续更新本人的学习记录, 跟随我一起不断学习.
✨ 感谢你们的耐心阅读, 博主本人也是一名学生, 也还有需要很多学习的东西. 写这篇文章是以本人所学内容为基础, 日后也会不断更新自己的学习记录, 我们一起努力进步, 变得优秀, 小小菜鸟, 也能有大大梦想, 关注我, 一起学习.
再次感谢你们的阅读, 你们的鼓励是我创作的最大动力!!!!!