在一些应用场合中,我们需要用到精准定时,比如每过一段时间,就执行固定一段代码。今天就来介绍滴答滴答定时器。
初步的想法是构建一个线程,给一个开始或结束的判断标志,加到while循环的判断条件中,加上锁,使得线程安全。每隔一段时间就让线程进入等待(阻塞)状态—lock.wait(delay),等时间到了线程自动被唤醒进入就绪状态,然后我们执行我们想要执行的代码。
以下是SimpleDidaDida类代码
package com.mec.dida;
public abstract class SimpleDidaDida implements Runnable {
public static final long DELAY = 1000;
public long delay;
public volatile boolean goon;
private Object lock;
public SimpleDidaDida() {
this(DELAY);
}
public SimpleDidaDida(long delay) {
this.lock = new Object();
this.delay = delay;
}
public void start() {
if(goon == true) {
return;
}
goon = true;
new Thread(this).start();
}
public void stop() {
if(goon ==false) {
return;
}
goon = false;
}
public abstract void doing();
@Override
public void run() {
while(goon) {
synchronized (lock) {
try {
lock.wait(delay);
doing();
System.out.println("时间:" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
测试Test代码
package com.mec.dida;
public class Test {
private SimpleDidaDida dida;
public Test() {
dida = new SimpleDidaDida(1000) {
@Override
public void doing() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
dida.start();
}
public static void main(String[] args) {
Test test = new Test();
}
}
测试结果如下:
我们可以看到实际线程并没有按照我们的设想,每个1秒执行一次doing(),因为执行doing()方法所用的时间也算入了我们的定时时间,这样可不行,就乱套了。
第二次设想:分析上述结果,我们发现只要将doing()方法放进循环里,就一定会占用一定的时间,但是它又必须在循环里,不然,这个定时器就毫无意义了!
所以我的想法是,构造一个实现了Runnable接口的内部类,我们将线程的启动直接放到内部类无参构造中,并在run()中执行doing()方法。
以下是DidaDida的代码
package com.mec.dida;
public abstract class DidaDida implements Runnable {
public static final long DELAY = 1000;
public long delay;
public volatile boolean goon;
private Object lock;
public DidaDida() {
this(DELAY);
}
public DidaDida(long delay) {
this.lock = new Object();
this.delay = delay;
}
public void start() {
if(goon == true) {
return;
}
goon = true;
new Thread(this).start();
}
public void stop() {
if(goon ==false) {
return;
}
goon = false;
}
public abstract void doing();
@Override
public void run() {
while(goon) {
synchronized (lock) {
try {
lock.wait(delay);
new DidaSon();
System.out.println("时间:" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private class DidaSon implements Runnable {
public DidaSon() {
new Thread(this).start();
}
@Override
public void run() {
doing();
}
}
}
测试Test代码
package com.mec.dida;
public class Test {
DidaDida dida;
public Test() {
dida = new DidaDida(1000) {
@Override
public void doing() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
dida.start();
}
public static void main(String[] args) {
Test sim = new Test();
}
}
测试结果如下:
这个单位是以毫秒为单位的!可以看到即使我们在doing()方法中让线程进入阻塞态500ms,对于我们定时器的影响也只有0~2ms的延时差,因为毕竟我们也进行了初始化操作new DidaSon();
Lock实现
package com.mec.didadida.core;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public abstract class DidaDidaL implements Runnable {
public static final long TIME_OUT = 3000;
private Lock lock;
private long timeout;
private boolean goon;
public Condition condition;
private ThreadPoolExecutor threadPool;
public DidaDidaL() {
this(TIME_OUT);
}
public DidaDidaL(long timeout) {
this.timeout = timeout;
lock = new ReentrantLock();
condition = lock.newCondition();
threadPool =
new ThreadPoolExecutor(5, 7, TIME_OUT, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<Runnable>());
}
public void setTimeout(long timeout) {
this.timeout = timeout;
}
public void start() {
if(!goon) {
goon = true;
new Thread(this).start();
}
}
public void stop() {
if(goon) {
goon = false;
}
}
public abstract void doing();
@Override
public void run() {
while(goon) {
lock.lock();
try {
condition.await(timeout, TimeUnit.MILLISECONDS);
//threadPool.execute(new DidaDoing());
new DidaDoing();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
class DidaDoing implements Runnable {
public DidaDoing() {
new Thread(this).start();
}
@Override
public void run() {
doing();
}
}
}
到这里,我们的定时器滴答滴答的叙述完啦!
综上:这只是线程应用的一个小技巧,以后还有更多需要学习的内容!