目录
定时器
实现定时器
描述任务
保存任务
扫描任务
执行任务
定时器
在实现定时器之前,先来简单的了解一下什么是定时器。
定时器是软件开发中一个重要的组件。比如到了什么时候,干一件什么样的事情;多少秒之后干什么。本篇文章介绍的多长时间后干什么事情。
Java标准库中的定时器,所提供的类——Timer 核心方法是schedule()。这里只演示多久之后执行任务这个方法。
import java.util.Timer;
import java.util.TimerTask;
public class ThreadDemo25 {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("这是任务1, 1000ms后执行");
}
}, 1000);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("这是任务2, 2000ms后执行");
}
}, 2000);
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println("这是任务3,3000ms后执行");
}
};
timer.schedule(task, 3000);
}
}
这里schedule中的两个参数
一个是TimeTask task 描述具体要执行的任务
一个是long time 描述time时间后执行该任务
其中TimeTask是一个抽象类,实现了Runnable接口。可以简单的理解为是堆Runnable接口的升级,实际上还是Runnable接口。
实现定时器
实现的是多久之后执行任务这一定时器。
描述任务
这里需要一个类是描述这个任务,因为除了任务之外,还需要时间。
// 使用Task类描述任务
// 因为存储该任务使用的是优先级阻塞队列,要比出优先级需要有比较器
// 这里直接实现Comparable接口
class Task implements Comparable<Task> {
// 时间
private long after;
// 任务
private Runnable runnable;
public Task(Runnable runnable, long after) {
this.runnable = runnable;
this.after = after;
}
@Override
public int compareTo(Task task) {
return (int)(this.after - task.after);
}
public long getAfter() {
return after;
}
public void run(){
this.runnable.run();
}
}
保存任务
这里使用PriorityBlockingQueue来保存任务。因为保存进去的时候会按照时间来保存。带有优先级的阻塞队列完美符合这一点。检查是否要执行任务,只需要查看队首的元素时间是不是到了。
// 使用优先级阻塞队列保存任务
private PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();
扫描任务
定时器创建时(所以需要在构造方法中),需要一个线程一直扫描检查里面的任务时间是否到了,到了就立马执行。
// 扫描线程时间
private Thread scan = null;
执行任务
执行任务根据时间来判断是否要执行。如果时间还没到的时候,那么线程应该阻塞才好。其中时间是不固定的。可能随时会有新的任务添加进来,这样就会导致时间的不确定性。这里使用wait和notify来让任务阻塞还是执行。如果有新的任务添加进来,就唤醒以下正在阻塞的扫描线程,看看新的时间是否要执行,如果时间还不到,就继续阻塞,知道最早的时间到了,在执行任务。
public MyTimer() {
// 创建一个线程扫描任务时间
scan = new Thread(() -> {
while (true) {
try {
long now = System.currentTimeMillis();
Task task = queue.take();
// 如果最快执行的任务的时间大于当前时间,就wait二者相差时间
// 否则将会一直取出来在放进去
if (task.getAfter() > now) {
queue.put(task);
synchronized (locker) {
locker.wait(task.getAfter() - now);
}
} else {
task.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
scan.start();
}
public void schedule (Runnable runnable, long after){
Task task = new Task(runnable, System.currentTimeMillis() + after);
queue.put(task);
synchronized (locker) {
locker.notify();
}
}
完整代码如下
// 实现一个定时器
import java.util.concurrent.PriorityBlockingQueue;
// 使用Task类描述任务
// 因为存储该任务使用的是优先级阻塞队列,要比出优先级需要有比较器
// 这里直接实现Comparable接口
class Task implements Comparable<Task> {
// 时间
private long after;
// 任务
private Runnable runnable;
public Task(Runnable runnable, long after) {
this.runnable = runnable;
this.after = after;
}
@Override
public int compareTo(Task task) {
return (int)(this.after - task.after);
}
public long getAfter() {
return after;
}
public void run(){
this.runnable.run();
}
}
class MyTimer {
// 扫描线程时间
private Thread scan = null;
// 使用优先级阻塞队列保存任务
private PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();
// 时间还没有到的时候
// 使用这个对象进行加锁和唤醒
private Object locker = new Object();
// 构造方法
public MyTimer() {
// 创建一个线程扫描任务时间
scan = new Thread(() -> {
while (true) {
try {
long now = System.currentTimeMillis();
Task task = queue.take();
// 如果最快执行的任务的时间大于当前时间,就wait二者相差时间
// 否则将会一直取出来在放进去
if (task.getAfter() > now) {
queue.put(task);
synchronized (locker) {
locker.wait(task.getAfter() - now);
}
} else {
task.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
scan.start();
}
public void schedule (Runnable runnable, long after){
Task task = new Task(runnable, System.currentTimeMillis() + after);
queue.put(task);
synchronized (locker) {
locker.notify();
}
}
}
public class ThreadDemo26 {
public static void main(String[] args) {
MyTimer myTimer = new MyTimer();
myTimer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("这是任务1");
}
}, 6000);
myTimer.schedule(new Runnable() {
@Override
public void run() {
System.out.println("这是任务2");
}
}, 2000);
}
}
有什么问题评论区指出。希望可以帮到你。