定时器可以帮助我们监管一部分的任务,只需要在规定的时间内自动执行,可以提高我们平常时间效率,下面来一起看看如何实现简单的java定时器
一、定时器
定时器相当于一个任务管理器。有些任务可能现在执行, 有些任务可能过1个小时,甚至很久才会执行。定时器就是对这些任务进行管理监视, 如果一个任务执行时间到了,定时器就会将这个任务执行。 保证所有的任务都会在合适的时间执行。
二、定时器的实现
1、 使用一个MyTask类描述每一个任务
2、 使用优先级队列管理这些任务类。
我们都知道优先级队列底层实现是堆(以小根堆为例), 堆顶的元素是所有的元素的最小值。 我们以任务的定时时间为比较原则构建, 这样就可以保证堆顶元素的任务执行时间是最短的(这样的实现,我们需要在Task类内部定义比较规则-即重写Comparable接口的CompareTo方法),当一个任务执行完毕, 就会从优先级队列取出掉, 然后内部重新组织保证新的堆顶元素是定时时间最短的。
3、使用一个线程循环扫描优先级队列, 相当于一个监控线程,循环判断堆顶任务是否满足执行时间。
三、定时器的组成
1.任务类MyTask
//描述定时器的任务
class MyTask implements Comparable<MyTask> {
private Runnable runnable;
//记录定时器的执行时间
private long time;
public Runnable getRunnable() {
return runnable;
}
public void setRunnable(Runnable runnable) {
this.runnable = runnable;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public MyTask(Runnable runnable, long time) {
if (time < 0) {
throw new RuntimeException("延迟时间不能小于等于0");
}
this.runnable = runnable;
//time记录的是任务的具体执行时间
this.time = time + System.currentTimeMillis();
}
@Override
public int compareTo(MyTask task) {
if (this.time == task.time) {
return 0;
} else if (this.time > task.time) {
return 1;
} else {
return -1;
}
// return (int) (this.time-task.getTime());
}
}
2、扫描线程&定时器对象MyTimer
扫描线程存在一个缺点,就是它会一直扫描监控,(例如队首任务是一小时后,那么它在一小时内会不断的循环扫描),很浪费cpu的资源,这个缺点我们可以通过线程阻塞和唤醒来解决。如果线程判断出当前任务需要1一小时后执行,那么我们让扫描线程wait等待一小时,如果有其他的任务添加进线程,可以重新唤醒线程来进行扫描判断,所以我们定义一个Object类的锁对象来实现阻塞唤醒。
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit;
public class MyTimer {
//使用优先级队列来保存任务
private BlockingQueue<MyTask> queue = new PriorityBlockingQueue<>();
//定义一个锁对象
Object locker = new Object();
//定义一个添加任务的方法
public void schedule(Runnable runnable, long time) throws InterruptedException {
MyTask myTask = new MyTask(runnable, time);
queue.put(myTask);
synchronized (locker) {
locker.notifyAll();
}
}
//构造方法
public MyTimer() {
//创建扫描线程
Thread thread = new Thread(() -> {
//不停的从从队列中取出任务
while (true) {
try {
//1.取出任务
MyTask take = queue.take();
//2.判断任务的执行时间是不是到达
long currenttime = System.currentTimeMillis();
if (currenttime >= take.getTime()) {
//如果时间到了执行任务
take.getRunnable().run();
} else {
//如果时间没到,写入队列
queue.put(take);
//计算一下任务执行时间与当前时间的差值
long waittime = take.getTime() - currenttime;
synchronized (locker) {
locker.wait(waittime);
}
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
thread.start();
Thread threaddaemon = new Thread(() -> {
while (true) {
synchronized (locker) {
locker.notifyAll();
}
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
threaddaemon.setDaemon(true);
threaddaemon.start();
}
}
3、测试代码
其中添加了5个任务, 分别是10s、1s、2s、8s、14s后执行。
public class Demo_Timer04 {
public static void main(String[] args) throws InterruptedException {
MyTimer myTimer=new MyTimer();
myTimer.schedule(()->{
System.out.println("任务1....10s");
},10000);
myTimer.schedule(()->{
System.out.println("任务2....1s");
},1000);
myTimer.schedule(()->{
System.out.println("任务3....2s");
},2000);
myTimer.schedule(()->{
System.out.println("任务4....8s");
},8000);
myTimer.schedule(()->{
System.out.println("任务5....14s");
},14000);
}
}
任务2....1s
任务3....2s
任务4....8s
任务1....10s
任务5....14s
执行如下,各个任务按时间执行。