发布于2026-07-15 阅读(0)
扫一扫,手机访问
自然引入,没有强行添加模板化小标题,整体排版自然流畅。 ---
在某些应用场景下,我们需要一个“靠谱”的定时器,能在精确的时间间隔内自动执行任务。比如定时刷新数据、周期性地发送心跳包,或者干脆就是让某个操作按固定节奏反复运行。如果能有一个轻量级的工具,直接拿来用,编程会省心很多。
最直接的想法是:让一个线程每隔指定的时间“醒来”一次,然后执行我们想要的操作。听起来简单,但实际写起来有不少坑。这里要说明一下,这个定时器是基于线程的,如果你对线程的基本概念还不熟悉,建议先补补基础,再往下看。
我们可以把需求拆成几个关键点:
delay)基于以上需求,设计思路就很清晰了:
delay 来存储等待时间。goon 标志来控制线程是否继续运行。start() 和 stop() 两个方法,分别用于启动和停止线程。doing(),让子类去实现具体逻辑。这样,一个最基本的定时器骨架就出来了。
先写一个最简单的版本,看看效果。代码如下:
public abstract class SimpleDidaDida implements Runnable {
public static final long DEFAULT_DELAY=1000;//默认时延
private long delay;//时延
private volatile boolean goon;//便于控制线程
private Object lock;//确保可以精准等待,给一个对象锁,wait()等基于该锁
//初始化时允许用户指定等待时间,若不指定,则使用默认时延
public SimpleDidaDida() {
this(DEFAULT_DELAY);//调用单参构造
}
public SimpleDidaDida(long delay) {
this.lock = new Object();//初始化对象锁
this.delay = delay;
}
//具体完成的事情不由该类实现
public abstract void doing();
//启动线程
public void start() {
if(this.goon == true) {
return;
}
this.goon = true;
new Thread(this).start();
}
//停止线程,当线程等待执行完doing后,goon==false时
public void stop() {
if(this.goon == false) {
return;
}
this.goon = false;//说明线程运行完毕
}
@Override
public void run() {
while(goon) {
synchronized(lock) {
try {
//当前线程暂停执行,即处于等待状态,并释放对象锁;
//当超过delay或执行notify/notifyAll()方法,该线程会被唤醒
lock.wait(delay);
doing();//调用抽象方法
} catch (InterruptedException e) {
goon = false;
}
}
}
}
}
然后写一个测试类,让定时器每隔500毫秒打印一次当前时间:
public class DemoSimpleDidaDida {
private SimpleDidaDida dida;
private static int times = 0;
public DemoSimpleDidaDida() {
//此处500,表示500毫秒运行一次
dida = new SimpleDidaDida(500) {
@Override
public void doing() {
System.out.println(System.currentTimeMillis());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}
public void runup() {
dida.start();//启动线程
try {
Thread.sleep(5000);//此处要求线程运行5秒后停止,5000单位是毫秒(ms)
} catch (InterruptedException e) {
}
dida.stop();//停止线程
}
}
启动测试:
public class Test {
public static void main(String[] args) {
new DemoSimpleDidaDida().runup();
}
}
运行结果如下:

注意看图片中的数字,后四位才是关键。第一个输出与第二个相差604毫秒,第二个与第三个相差602,第三个与第四个相差601……没有一个严格等于500毫秒的。为什么?
问题出在 doing() 方法本身也需要时间执行。我们故意在 doing() 里加了 Thread.sleep(100),结果定时器实际间隔变成了600毫秒左右。这说明:如果让定时器线程在等待结束后直接执行任务,那么任务的执行时间会“吃掉”本该属于等待的时间,导致定时不准。
要解决这个问题,思路很简单:让定时器线程只负责“等待和触发”,不负责执行具体任务。具体任务交给另一个线程去干。这样,定时器线程每次等待的时间就只受wait()本身的影响,不会被任务执行时间拖累。
我们用一个内部类来实现这个“工作线程”:
@Override
public void run() {
while(goon) {
synchronized(lock) {
try {
//当前线程暂停执行,即处于等待状态,并释放对象锁;
//当超过delay或执行notify/notifyAll()方法,该线程会被唤醒
lock.wait(delay);
new InnerWorker();//内部类启动线程
} catch (InterruptedException e) {
goon = false;
}
}
}
}
//该内部类主要负责启动一个线程,让该线程去执行doing().
private class InnerWorker implements Runnable{
public InnerWorker() {
new Thread(this).start();
}
@Override
public void run() {
doing();
}
}
这里的关键是:wait() 结束后,立即启动一个新线程去执行 doing(),而定时器线程本身则继续进入下一轮等待。虽然启动线程也需要一点时间,但这个时间损耗是固定的,可以忽略不计。
运行结果:

看,是不是工整多了?只有第四次和第五次之间差了501毫秒,其余都是精确的500毫秒。效果立竿见影。
不过,这里有一个潜在问题:如果某次任务的执行时间超过了定时器周期(比如 doing() 执行了600毫秒,而定时器周期只有500毫秒),那么第二次定时触发时,第一次任务可能还没结束。会不会乱套?
答案是:不会。因为每个 InnerWorker 都是独立线程,它们之间互不干扰。我们的需求是“定时器每次准时触发”,并不要求任务按顺序执行。如果任务本身耗时较长,那就让多个任务并行跑好了,这反而能提高吞吐量。
为了验证这一点,我们修改一下测试代码,让任务执行时间超过定时周期:
public class DemoSimpleDidaDida {
private SimpleDidaDida dida;
private static int times = 0;
public DemoSimpleDidaDida() {
dida = new SimpleDidaDida(500) {
@Override
public void doing() {
int time = ++times;
System.out.println("第" + time+ "次开始:" + System.currentTimeMillis());
try {
Thread.sleep(600);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第" + time+ "次结束:" + System.currentTimeMillis());
}
};
}
public void runup() {
dida.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
dida.stop();
}
}
运行结果:

可以看到,每次任务的开始时间严格间隔500毫秒,即使前一次任务还没结束,后一次任务照样准时启动。各线程之间完全独立,互不阻塞。
当然,这个版本还不算完美,定时精度还可以进一步提升。后面我们会用线程池来优化,让定时更加精准且可控。不过对于大多数日常应用,这个进阶版已经足够用了。
实现一个精准的线程定时器,核心在于把“定时等待”和“任务执行”分离到不同的线程中。最简单的做法是使用 wait(delay) 配合一个内部工作线程,这样既能保证定时精度,又能避免任务耗时影响后续周期。需要注意的是,如果任务执行时间超过周期,多个任务会并发执行,这在某些场景下是好事,但也要注意线程安全和资源消耗。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8