java定时器(java.util.Timer)可以方便,高效地帮助我们完成一些周期性的操作,比如每天零点整对数据初始化操作等等。通过调用Timer内部的schedule(TimerTask task, Date firstTime, long period)方法,其中第一个参数是TimerTask类型,它“实现”了Runnable接口,但依然是抽象的,需要我们把执行的任务写在Run()方法里。第二个参数是任务开始执行的时间,第三个参数是重复执行的间隔时间。当然,Timer类还提供了很多控制方法等等,这里就不再一一介绍了。
下面我们来模拟实现一下Timer的schedule()方法。这里很容易想到用sleep(long t)方法来做定时器,但是这样间隔时间就变成了t + 任务执行时间,所以下一次执行的具体时间无法确定。这明显不符合我们定时器的要求,所以采用多线程来解决。即,用一个线程专门负责执行任务,另一个线程负责计时,时间间隔一到,就唤醒执行任务的线程。至于任务需要执行多长时间都不会影响计时,也就保证了在固定时间间隔内重复执行任务。根据这种思路,模拟实现了我们自己的定时器类DIdaDida。
public class DidaDida implements Runnable {
private long delayTime;
private long period;
private Object lock;
private volatile boolean goon;
private DidaDidaAction action;
public DidaDida(boolean T) {
if(T) {
goon = true;
lock = this;
}
}
//在指定的时间后执行任务(也可以用Date类型在指定的时间执行任务);
//执行一次
public void schedule(DidaDidaAction action, long delayTime) {
if (!goon) {
System.out.println("定时器未开启");
return;
}
if (action == null) {
System.out.println("无任务!");
return;
}
if(delayTime <= 0) {
System.out.println("延时不能小于或等于0!");
}
this.action = action;
this.delayTime = delayTime;
this.period = 0;
new Thread(new TimeWorker(), "TimeWork").start();
new Thread(this, "DidaDida").start();
}
//在指定的时间后执行任务;
//固定延时重复执行
public void schedule(DidaDidaAction action, long delayTime, long period) {
if(!goon) {
System.out.println("定时器未开启");
return;
}
if (action == null) {
System.out.println("无任务!");
return;
}
if(delayTime <= 0) {
System.out.println("延时不能小于或等于0!");
return;
}
if(period <= 0) {
System.out.println("延时不能小于或等于0!");
return;
}
this.action = action;
this.delayTime = delayTime;
this.period = period;
new Thread(new AlwaysTimeWorker(), "AlwaysTimeWork").start();
new Thread(this, "DidaDida").start();
}
public void stop() {
if (action == null) {
System.out.println("没做任何事!");
return;
}
if (goon == false) {
System.out.println("时钟已经停止计时!");
return;
}
goon = false;
}
@Override
public void run() {
//只进行一次计时
if(period <= 0) {
synchronized (lock) {
try {
lock.wait(delayTime);
lock.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return;
}
//循环计时
else {
while (goon) {
// 这里是定时器,而且,它只定时!
synchronized (lock) {
try {
lock.wait(period);
// 这里应该唤醒一个线程,这个线程专心负责执行任务!
lock.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
//重复执行任务
class AlwaysTimeWorker implements Runnable {
public AlwaysTimeWorker() {
}
@Override
public void run() {
while(goon) {
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
action.run();
}
}
}
}
//只执行一次任务
class TimeWorker implements Runnable {
public TimeWorker() {
}
@Override
public void run() {
while (goon) {
try {
synchronized (lock) {
lock.wait();
}
action.run();
goon = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
下面是测试代码:
public class test {
public static void main(String[] args) {
DidaDida dida = new DidaDida(true);
dida.schedule(new DidaDidaTask() {
@Override
public void run() {
System.out.println("每隔一秒输出一次时间:" + System.currentTimeMillis());
}
}, 3000, 1000);
}
}
结果如下:
每隔一秒输出一次时间:1572065901838
每隔一秒输出一次时间:1572065902838
每隔一秒输出一次时间:1572065903839
每隔一秒输出一次时间:1572065904839
每隔一秒输出一次时间:1572065905840
每隔一秒输出一次时间:1572065906841
每隔一秒输出一次时间:1572065907842
每隔一秒输出一次时间:1572065908843
每隔一秒输出一次时间:1572065909843
每隔一秒输出一次时间:1572065910844
每隔一秒输出一次时间:1572065911844
每隔一秒输出一次时间:1572065912845
可以看出确实做到了定时功能,但是仍然存在0.0001秒的误差。