【JavaEE】【多线程】定时器
目录
一、定时器简介
定时器:就相当于一个闹钟,当我们定的时间到了,那么就执行一些逻辑。
1.1 Timer类
Java的标准库中提供了在java.util包下的Timer类作为定时器。
有如下的构造方法:
四种:
- timer() 无参构造;
- timer(boolean isDaemon) 创建的线程都是后台线程;
- timer(String name) 给定时器中创建的线程名字;
- timer(String name, boolean isDaemon) 创建的线程都是后台线程,也给定时器中创建的线程名字。
在Timer类中的核心方法是schedule方法。
- schedule(Timer task, Date time) 到达time时刻后执行task任务;
- schedule(Timer task, Date firstTime, long period) 到达time时刻后重复执行task任务,每次相隔period时间;
- schedule(Timer task, long delay) 在delay时间后执行task任务;
- schedule(Timer task, long delay, long period) 在delay时间后重复执行task任务,每次相隔period时间;
- scheduleAtFixedRate(Timer task, Date firstTime, long period) 到达time时刻后重复执行task任务,每次执行period时间;
- scheduleAtFixedRate(Timer task, long delay, long period) 在delay时间后重复执行task任务,每次执行period时间;
schedule的第一个参数是TimerTask类,这是一个实现了Runnable接口的抽象类。
1.2 使用案例
我们使用schedule方法来打印不同时间执行不同内容。
import java.util.Timer;
import java.util.TimerTask;
public class Demo {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("3000ms后执行");
}
},3000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("1000ms后执行");
}
},1000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("2000ms后执行");
}
},2000);
}
}
结果如下:会按照等待时间由小到大打印内容,并且执行完之后并不会结束,这是因为这些线程是前台线程。
二、实现简易定时器
自己实现的定时器主要要考虑下面几个内容:
- 设计一个类表示任务,对应TimerTask类;
- 使用优先级队列来组织多个任务,每次根节点都是等待时间最短的任务;
- 实现schedule方法,把任务添加到队列中;
- 额外创建一个线程,负责执行队列中的任务,根据时间来执行(即判断是否到了该执行的时间了)。
2.1 MyTimerTask类
这个类中需要:
代码:
class MyTimerTask implements Comparable<MyTimerTask>{
//记录任务
private Runnable task = null;
//记录执行任务的时刻
private long current = 0;
public MyTimerTask(Runnable task, long current) {
this.task = task;
this.current = current;
}
public Runnable getTask() {
return task;
}
public long getCurrent() {
return current;
}
@Override
public int compareTo(MyTimerTask o) {
return (int)(this.current - o.current);
}
}
2.2 实现schedule方法
我们实现schedule方法:
代码:
private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
public void schedule(Runnable task, long delay) {
synchronized (this) {
MyTimerTask myTimerTask = new MyTimerTask(task, System.currentTimeMillis() + delay);
queue.offer(myTimerTask);
this.notify();
}
}
2.3 构造方法
在构造方法中额外创建一个线程,负责执行队列中的任务,根据时间来执行(即判断是否到了该执行的时间了)。
代码:
public MyTimer() {
Thread thread = new Thread(()-> {
try {
while(true) { //循环拿任务,直到任务队列为空
synchronized (this) {
while (queue.isEmpty()) { //任务队列为空
this.wait();
}
MyTimerTask task = queue.peek();
if(task.getCurrent() > System.currentTimeMillis()) { //没到执行时间
this.wait(task.getCurrent() - System.currentTimeMillis());
} else {
task.run();
queue.poll();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
}
2.4 总代码
总代码如下:
class MyTimerTask implements Comparable<MyTimerTask>{
//记录任务
private Runnable task = null;
//记录执行任务的时刻
private long current = 0;
public MyTimerTask(Runnable task, long current) {
this.task = task;
this.current = current;
}
public Runnable getTask() {
return task;
}
public long getCurrent() {
return current;
}
@Override
public int compareTo(MyTimerTask o) {
return (int)(this.current - o.current);
}
public void run() {
task.run();
}
}
class MyTimer {
private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
public void schedule(Runnable task, long delay) {
synchronized (this) {
MyTimerTask myTimerTask = new MyTimerTask(task, System.currentTimeMillis() + delay);
queue.offer(myTimerTask);
this.notify();
}
}
public MyTimer() {
Thread thread = new Thread(()-> {
try {
while(true) { //循环拿任务,直到任务队列为空
synchronized (this) {
while (queue.isEmpty()) { //任务队列为空
this.wait();
}
MyTimerTask task = queue.peek();
if(task.getCurrent() > System.currentTimeMillis()) { //没到执行时间
this.wait(task.getCurrent() - System.currentTimeMillis());
} else {
task.run();
queue.poll();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
}
}
2.5 测试
如果在main中执行下面这样的代码,也使用schedule方法来打印不同时间执行不同内容,会与上面使用案例的结果一样。
public static void main(String[] args) {
MyTimer timer = new MyTimer();
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("3000ms后执行");
}
},3000);
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("1000ms后执行");
}
},1000);
timer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("2000ms后执行");
}
},2000);
}
结果如下:会按照等待时间由小到大打印内容,并且执行完之后并不会结束,这是因为这些线程是前台线程。
作者:鸽鸽程序猿