在 Java 开发中,实现延时的需求非常常见,例如定时任务、轮询检测、请求限流等场景,Java 提供了多种延时方法,每种方法都有其适用场景和特点,本文将详细介绍 Java 中实现延时的几种主要方式,包括其原理、使用方法和注意事项,帮助开发者根据实际需求选择合适的方案。

Thread.sleep():基础线程休眠
Thread.sleep() 是 Java 中最基础的延时方法,它属于 java.lang.Thread 类,该方法的作用是让当前执行的线程暂停指定的时间(毫秒或纳秒),期间线程不会占用 CPU 资源,进入阻塞状态,直到休眠时间结束再继续执行。
使用方法
Thread.sleep() 提供了两个重载方法:
sleep(long millis):让线程休眠指定的毫秒数。sleep(long millis, int nanos):让线程休眠指定的毫秒数和纳秒数(纳秒部分需在 0-999999 之间)。
示例代码:
public class SleepExample {
public static void main(String[] args) {
System.out.println("开始时间:" + System.currentTimeMillis());
try {
Thread.sleep(2000); // 休眠2秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束时间:" + System.currentTimeMillis());
}
}
注意事项
- 异常处理:
sleep()方法会抛出InterruptedException,当线程在休眠期间被其他线程中断时,该异常会被触发,因此必须使用try-catch捕获。 - 线程阻塞:调用
sleep()后,线程会进入TIMED_WAITING状态,释放 CPU 资源,但不会释放锁对象。 - 时间精度:
sleep()的实际休眠时间可能略长于指定时间,受系统调度和线程优先级影响,不适合高精度延时场景。
TimeUnit 中的 sleep():更友好的时间单位
Java 5 引入了 java.util.concurrent.TimeUnit 类,它提供了更直观的时间单位转换方法,sleep() 方法是对 Thread.sleep() 的封装,支持秒、分、时等多种时间单位,代码可读性更高。
使用方法
TimeUnit 提供了 sleep(long timeout) 方法,参数 timeout 为时间数值,结合 TimeUnit 枚举指定时间单位。
示例代码:

import java.util.concurrent.TimeUnit;
public class TimeUnitSleepExample {
public static void main(String[] args) {
System.out.println("开始延时...");
try {
TimeUnit.SECONDS.sleep(3); // 休眠3秒
TimeUnit.MINUTES.sleep(1); // 休眠1分钟
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("延时结束");
}
}
优势
- 可读性强:直接使用
SECONDS、MINUTES等枚举,避免手动计算毫秒数,减少代码错误。 - 统一管理:
TimeUnit还提供了其他时间转换方法(如toMillis()),方便时间单位统一处理。
Object.wait():基于锁的等待
Object.wait() 是 Java 中用于线程间通信的方法,它可以让当前线程等待,直到其他线程调用 notify() 或 notifyAll() 唤醒,通过结合 wait(long timeout) 方法,可以实现延时功能,但需要注意它必须配合锁(synchronized 代码块或方法)使用。
使用方法
wait(long timeout) 表示线程等待指定时间(毫秒),超时后自动唤醒,但唤醒后仍需重新获取锁。
示例代码:
public class WaitExample {
private static final Object lock = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (lock) {
try {
System.out.println("线程开始等待...");
lock.wait(3000); // 等待3秒
System.out.println("线程被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
注意事项
- 必须配合锁:
wait()必须在synchronized代码块或方法中调用,否则会抛出IllegalMonitorStateException。 - 唤醒机制:
wait()会释放锁,而sleep()不会,如果线程被提前唤醒,wait()会立即结束等待;若未被唤醒,则超时后自动唤醒。 - 适用场景:适用于需要线程同步和唤醒机制的延时场景,而非简单的延时。
ScheduledExecutorService:定时任务线程池
对于需要重复执行或精确调度的延时任务,ScheduledExecutorService 是更合适的选择,它是 Java 并发包(java.util.concurrent)提供的线程池,支持延时执行、固定频率执行等任务调度。
核心方法
schedule(Runnable command, long delay, TimeUnit unit):延时delay时间后执行一次任务。scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit):延时initialDelay后首次执行,之后每隔period时间重复执行。scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit):首次执行后,每次任务结束后等待delay时间再执行下一次。
示例代码:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledExecutorExample {
public static void main(String[] args) {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
// 延时2秒后执行一次任务
executor.schedule(() -> {
System.out.println("延时任务执行:" + System.currentTimeMillis());
}, 2, TimeUnit.SECONDS);
// 延时1秒后首次执行,之后每3秒执行一次
executor.scheduleAtFixedRate(() -> {
System.out.println("固定频率任务:" + System.currentTimeMillis());
}, 1, 3, TimeUnit.SECONDS);
// 关闭线程池(实际开发中需根据需求决定是否关闭)
// executor.shutdown();
}
}
优势
- 功能强大:支持单次、重复、固定频率等多种调度模式。
- 资源管理:通过线程池管理任务,避免频繁创建和销毁线程,提高性能。
- 异常处理:任务执行中的异常不会影响其他任务,可通过
Future获取任务结果和异常。
Timer 和 TimerTask:传统定时任务
Timer 和 TimerTask 是 Java 早期提供的定时任务工具,Timer 负责调度任务,TimerTask 表示要执行的任务,虽然功能相对简单,但在简单延时场景中仍可使用。

使用方法
- 继承
TimerTask类,重写run()方法定义任务逻辑。 - 创建
Timer对象,调用schedule()方法调度任务。
示例代码:
import java.util.Timer;
import java.util.TimerTask;
public class TimerExample {
public static void main(String[] args) {
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println("Timer 任务执行:" + System.currentTimeMillis());
}
};
// 延时1秒后执行一次任务
timer.schedule(task, 1000);
// 延时2秒后首次执行,之后每4秒重复执行
// timer.schedule(task, 2000, 4000);
// 取消任务(可选)
// timer.cancel();
}
}
注意事项
- 单线程执行:
Timer内部使用单线程调度,如果某个任务执行时间过长,会影响后续任务的执行时间。 - 异常处理:
TimerTask中的异常未捕获会导致整个Timer线程终止,后续任务无法执行。 - 适用场景:适合简单的单次或重复延时任务,复杂场景推荐使用
ScheduledExecutorService。
Java 8 中的 CompletableFuture:异步延时
Java 8 引入了 CompletableFuture,它提供了强大的异步编程能力,通过 CompletableFuture.runAsync() 结合 delayedExecutor(),可以实现异步延时执行。
使用方法
CompletableFuture.delayedExecutor(long delay, TimeUnit unit) 返回一个延时执行的 Executor,结合 runAsync() 执行异步任务。
示例代码:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class CompletableFutureExample {
public static void main(String[] args) {
System.out.println("开始异步延时...");
CompletableFuture.runAsync(() -> {
System.out.println("异步任务执行:" + System.currentTimeMillis());
}, CompletableFuture.delayedExecutor(2, TimeUnit.SECONDS))
.thenRun(() -> {
System.out.println("异步任务完成");
});
// 主线程等待异步任务结束(实际开发中无需等待)
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
优势
- 异步非阻塞:任务在异步线程中执行,不会阻塞主线程。
- 链式调用:支持
thenApply、thenAccept等链式操作,方便组合多个异步任务。 - 灵活调度:可与
CompletableFuture的其他功能结合,实现复杂的异步流程控制。
如何选择合适的延时方法
| 方法 | 特点 | 适用场景 |
|---|---|---|
Thread.sleep() |
简单易用,线程阻塞 | 简单延时测试、顺序控制 |
TimeUnit.sleep() |
时间单位友好,可读性高 | 需要明确时间单位的简单延时 |
Object.wait() |
需配合锁,支持线程唤醒 | 线程间同步通信的延时等待 |
ScheduledExecutorService |
线程池管理,支持复杂调度 | 定时任务、重复执行、高并发场景 |
Timer/TimerTask |
传统工具,单线程调度 | 简单单次或重复延时任务 |
CompletableFuture |
异步非阻塞,支持链式调用 | 异步编程流程中的延时控制 |
在实际开发中,需根据任务类型(单次/重复)、是否需要线程同步、并发性能要求等因素选择合适的延时方法,简单延时测试可使用 Thread.sleep(),而定时任务推荐 ScheduledExecutorService,异步场景则优先考虑 CompletableFuture,正确选择延时方法,不仅能提高代码可读性和可维护性,还能优化系统性能。


















