在生活中,我们经常会定时执行某一项操作,这样就需要让线程暂停一段时间在继续运行,这篇文章就给大家介绍一下如何通过线程完成计时器。
第一种定时器:
try {
Thread.sleep(delayTime);
doSomething();
} catch (InterruptedException e) {
e.printStackTrace();
}
这种方案是最简单和最直接的,通过Thread.sleep()直接让线程休眠,但是在保证多线程安全的前提下,我们经常会用到锁,Thread.sleep()方法不会释放当前锁,就会使得在锁里运行Thread.sleep(),方法时,其他的线程会进入该锁的阻塞队列,造成了对CPU资源的浪费。因此这样的做法很不完善。
第二种定时器:
@Override
public void run() {
while (goon) {
synchronized (lock) {
try {
lock.wait(delayTime);
doSomething();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
对于lock.wait(delayTime)这个定时器而言,由于它每次自动唤醒后都要继续做doSomething();的操作,以至于每次执行doSomething 的时间至少是等待的时间在加上doSomething操作本身需要的时间。定时不够准确。
第三种定时器:
@Override
public void run() {
while (goon) {
// 这里才是真正的定时器,而且,它只定时!
synchronized (lock) {
try {
lock.wait(delayTime);
// 这里应该唤醒一个线程,这个线程专心负责执行Action!
lock.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class TimeWorker implements Runnable {
public TimeWorker() {
}
@Override
public void run() {
while (goon) {
try {
synchronized (lock) {
lock.wait();
}
action.doSomething();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
这种方式是用线程配合来完成定时器的,我们用DidaDida这个类做专门的定时器,内部类TimeWorker专门处理doSomething。
假设TimeWork这个线程先开始工作,执行了lock.wait();方法后就自我阻塞了。定时线程获取锁之后进入工作状态后,执行lock.wait(delayTime);,等待delayTime毫秒之后,唤醒TimeWorker这个线程,定时线程继续循环,进入下一次延时。而另一边,TimeWork线程一旦或得了CPU资源后,就开始执行doSomething的操作,执行完毕之后,继续等待被唤醒,这样定时线程只专一的做一件事 —— 定时,而TimeWork只专心的处理doSomething。
即便是doSomething这个操作的时间非常长,在它执行一半的时候时间片段到了,定时线程再一次执行了lock.wait(delayTime);,这样也并不会影响定时器对时间的延迟
即便是几次优化其实还是有很多不精确的地方,比如TimeWork线程在被唤醒之后如果一直不能竞争上CPU资源,就无法执行,其等待的时间一样会比设置的时间长。
下面是DidaDida的完成类:
package com.mec.timer.didadida;
public class DidaDida implements Runnable {
public static final long DEFAULT_DELAY_TIME = 1000;
private long delayTime;
private Object lock;
private volatile boolean goon;
private IDidaDidaAction action;
public DidaDida() {
this(DEFAULT_DELAY_TIME);
}
public DidaDida(long delayTime) {
this(delayTime, null);
}
public DidaDida(long delayTime, IDidaDidaAction action) {
this.lock = new Object();
this.delayTime = delayTime;
this.action = action;
}
public void setAction(IDidaDidaAction action) {
this.action = action;
}
public void setDelayTime(long delayTime) {
this.delayTime = delayTime;
}
public void start() {
if (action == null) {
System.out.println("无事可做!");
return;
}
if (goon) {
return;
}
goon = true;
new Thread(new TimeWorker(), "TimeWork").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() {
while (goon) {
// 这里才是真正的定时器,而且,它只定时!
synchronized (lock) {
try {
lock.wait(delayTime);
// 这里应该唤醒一个线程,这个线程专心负责执行Action!
lock.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class TimeWorker implements Runnable {
public TimeWorker() {
}
@Override
public void run() {
while (goon) {
try {
synchronized (lock) {
lock.wait();
}
action.doSomething();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}