定时器的作用:
不断循环指定的时间间隔,并且在时间间隔结束后能够执行用户指定的任务。
需求:
1、允许用户在指定时间间隔;
2、允许用户指定任务;
3、尽可能做到“精确定时”,消除误差;
4、将定时器实现成为工具;
首先,设置delayTime = 1000, Task任务为输出时间,接着使用Runnable接口的方式创建定时器主线程,主线程需要做的工作就是不断地休眠delayTime时长,并且在每次休眠结束时完成指定任务Task。
public class Test implements Runnable{
private boolean goon = true;
private int delayTime = 1000;
public Test() {
}
public void start() {
new Thread(this).start();
}
@Override
public void run() {
while(goon) {
try {
Thread.sleep(delayTime);
Task.run();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
public static class Task {
public static void run() {
System.out.println(System.currentTimeMillis());
}
}
}
如此做确实能够完成定时器的任务,但是定时器每次经过delayTime后还需要执行完成任务后才会进入一轮循环,从而产生时间误差。
时间间隔 = delayTime + 执行Task的时间;
为了消除这一误差,我们可以将需要执行的任务也制定为一个独立的线程,并且为任务线程上锁,使其进入阻塞态。当我们启动定时器主线程后,主线程先休眠delayTime,然后只需要打开任务线程的锁即可。
时间间隔 = delayTime + 启动Task的时间
同时为了线程的安全性,我们给主线程也加上锁,使两个线程不会冲突,并且在启动时,会先将任务线程预加载,随之上锁。
运行过程: 定时器启动 ---> 预加载任务线程 --> 任务线程进入阻塞态 --> 启动主线程
--> 休眠DelayTime -- > 唤醒任务线程、随即主线程进入阻塞态 --> 任务线程执行任务
在应用synchronized锁时,有一个因失误而造成的错误,使得在休眠后,Task任务只会被执行一次。
@Override //定时器主线程
public void run() {
while(goon) {
synchronized (lock) {
lock.notify();
try {
Thread.sleep(delayTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
这是因为我将sleep休眠放在了synchronized锁当中,而同一时间、同一把锁只能执行一个锁中的内容,致使在唤醒任务进程时,并没有进行“计时”,正确的做法应当时将sleep放置在锁外。从这一点中我们也可以看出synchronized锁所具有的特性。
完整代码:
public class Didadida implements Runnable{
private int delayTime = 1000;
private Runnable task;
private volatile boolean goon = false;
Object lock = new Object();
public Didadida() {
}
public Didadida(Runnable task) {
setTask(task);
}
public Didadida(int delayTime) {
setDelayTime(delayTime);
}
public Didadida(int delayTime, Runnable task) {
setTask(task);
setDelayTime(delayTime);
}
//设置间隔时间;
public void setDelayTime(int delayTime) {
this.delayTime = delayTime;
}
//设置任务;
public void setTask(Runnable task) {
this.task = task;
}
//启动程序
public void startUp() throws InterruptedException {
if(goon) {
return;
}
this.goon = true;
if(task == null) {
System.out.println("任务为空,无法启动线程......");
return;
}
synchronized (lock) {
new Thread(new StartTask()).start();
lock.wait();
}
new Thread(this, "定时器").start();
}
public void end() {
this.goon =false;
}
@Override //定时器主线程
public void run() {
while(goon) {
synchronized (lock) {
lock.notify();
try {
Thread.sleep(delayTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class StartTask implements Runnable{
public StartTask(){
new Thread(this).start();
}
@Override //任务线程
public void run() {
synchronized (lock) {
lock.notify();
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
while(goon) {
synchronized (lock) {
try {
task.run();
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}