定时器是软件开发中的一个重要组件,类似于一个"闹钟"当达到一个设定的时间之后,就执行某个指定好的代码(任务)。
Timer
JAVA标准库中已经为我们实现了一个定时器,我们直接new就行了。
Timer timer = new Timer();
Timer类中最重要的一个方法就是schedule(),这个方法用于设置定时器待执行的任务和执行任务的时间。
- public static void main(String[] args) {
- Timer timer = new Timer();
- //在3秒后打印3000
- timer.schedule(new TimerTask() {
- @Override
- public void run() {
- System.out.println("3000");
- }
- }, 3000);
- //在2秒后打印2000
- timer.schedule(new TimerTask() {
- @Override
- public void run() {
- System.out.println("2000");
- }
- }, 2000);
- //在1秒后打印1000
- timer.schedule(new TimerTask() {
- @Override
- public void run() {
- System.out.println("1000");
- }
- }, 1000);
-
- System.out.println("OK");
- }
可以发现在代码执行完毕后程序并没有结束,这是因为虽然主线程结束了但是Timer类中的线程在阻止程序结束,它们还在等待新的任务进来被执行。
为了可以更好的理解定时器的原理,下面进行简单的模拟实现。
模拟实现
首先我们需要先创建一个MyTimerTask类,该类主要用来保存待执行的任务,和执行该任务的时间点。
- class MyTimerTask {
- public Runnable runnable;
- //存储绝对时间,后期直接和当前时间比较大小就行
- public long time;
-
- public MyTimerTask(Runnable runnable, long time){
- this.runnable = runnable;
- this.time = time + System.currentTimeMillis();
- }
- }
接着写我们的定时器类MyTimer。首先我们应该先选择一种数据结构来存储所有的MyTimerTask,这里我推荐使用优先级队列,也就是堆。因为如果使用数组或链表来存储就需要你不断地遍历该数组/链表,而使用优先级队列就可以只检查队首元素是否到执行时间。
- public class MyTimer {
- private final PriorityQueue
priorityQueue = new PriorityQueue<>(); - }
因为我们使用了优先级队列所以我们需要让MyTimerTask类可以进行比较。
- class MyTimerTask implements Comparable
{ - ……
- @Override
- public int compareTo(MyTimerTask o) {
- return (int) (this.time-o.time);
- }
- }
接着我们需要实现一个schedule方法可以接收定时任务和时间,因为是多线程代码所以我们还应该加一个属性用来充当锁对象。
- public class MyTimer {
- private final PriorityQueue
priorityQueue = new PriorityQueue<>(); - private Object lock = new Object();
-
- public void schedule(Runnable runnable, long time) {
- synchronized (lock) {
- priorityQueue.add(new MyTimerTask(runnable, time));
- }
- }
- }
此时我们还需要一个自动检查队列的线程,而且该线程并不需要程序员来启动和创建,所以我们可以将它写在构造方法中。
- public MyTimer() {
- Thread t = new Thread(()->{
- while(true) {
- synchronized (lock) {
- if (priorityQueue.isEmpty()) {
- //堆为空
- }
- MyTimerTask myTimerTask = priorityQueue.peek();
- if (myTimerTask.time <= System.currentTimeMillis()) {
- //执行当前任务
- myTimerTask.runnable.run();
- //将当前任务移除
- priorityQueue.poll();
- }else {
- //时间没到
- }
- };
- }
- });
- t.start();
- }
此处我们可以用阻塞队列的思想,当堆为空或者执行时间没到就使用wait()进行等待。有人添加任务时就使用notify()进行唤醒线程。
- public class MyTimer {
- private final PriorityQueue
priorityQueue = new PriorityQueue<>(); - private Object lock = new Object();
-
- public void schedule(Runnable runnable, long time) {
- synchronized (lock) {
- priorityQueue.add(new MyTimerTask(runnable, time));
- lock.notify();
- }
- }
-
- public MyTimer() {
- Thread t = new Thread(()->{
- while(true) {
- synchronized (lock) {
- if (priorityQueue.isEmpty()) {
- //如果堆为空就阻塞等待
- try {
- lock.wait();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- MyTimerTask myTimerTask = priorityQueue.peek();
- if (myTimerTask.time <= System.currentTimeMillis()) {
- //执行当前任务
- myTimerTask.runnable.run();
- priorityQueue.poll();
- }else {
- //时间没到就阻塞等待
- try {
- lock.wait(myTimerTask.time-System.currentTimeMillis());
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- };
- }
- });
- t.start();
- }
- }
简单测试
- public static void main(String[] args) {
- MyTimer timer = new MyTimer();
- //在3秒后打印3000
- timer.schedule(()->{
- System.out.println("3000");
- }, 3000);
- //在2秒后打印2000
- timer.schedule(()->{
- System.out.println("2000");
- }, 2000);
- //在1秒后打印1000
- timer.schedule(()->{
- System.out.println("1000");
- }, 1000);
-
- System.out.println("OK");
- }
完整代码
- import java.util.PriorityQueue;
-
- class MyTimerTask implements Comparable
{ - public Runnable runnable;
- //存储绝对时间,后期直接和当前时间比较大小就行
- public long time;
-
- public MyTimerTask(Runnable runnable, long time){
- this.runnable = runnable;
- this.time = time + System.currentTimeMillis();
- }
-
- @Override
- public int compareTo(MyTimerTask o) {
- return (int) (this.time-o.time);
- }
- }
-
- public class MyTimer {
- private final PriorityQueue
priorityQueue = new PriorityQueue<>(); - private Object lock = new Object();
-
- public void schedule(Runnable runnable, long time) {
- synchronized (lock) {
- priorityQueue.add(new MyTimerTask(runnable, time));
- lock.notify();
- }
- }
-
- public MyTimer() {
- Thread t = new Thread(()->{
- while(true) {
- synchronized (lock) {
- if (priorityQueue.isEmpty()) {
- //如果堆为空就阻塞等待
- try {
- lock.wait();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- MyTimerTask myTimerTask = priorityQueue.peek();
- if (myTimerTask.time <= System.currentTimeMillis()) {
- //执行当前任务
- myTimerTask.runnable.run();
- priorityQueue.poll();
- }else {
- //时间没到就阻塞等待
- try {
- lock.wait(myTimerTask.time-System.currentTimeMillis());
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- };
- }
- });
- t.start();
- }
- }
评论记录:
回复评论: