线程睡眠sleep方法
- [sleep 方法](#sleep 方法)
- [sleep 与锁的关系代码示例](#sleep 与锁的关系代码示例)
- [sleep 期间被中断抛出异常](#sleep 期间被中断抛出异常)
- [sleep 方法的应用场景](#sleep 方法的应用场景)
sleep 方法
当当前执行线程调用sleep方法时,该线程会主动放弃 CPU 的执行权并进入TIMED_WAITING(计时等待)状态(属于阻塞状态的子集),且在指定的时间范围内不再参与 CPU 的抢占式调度;在此期间,该线程不会释放已持有的任何对象监视器锁或其他同步资源;当指定的等待时间耗尽,或线程被其他线程通过 interrupt() 方法中断时,该方法会终止等待:
- 若时间耗尽:线程从 TIMED_WAITING 状态切换至 RUNNABLE(就绪)状态,等待 CPU 调度器分配执行时间片后恢复执行;
- 若被中断:方法会立即抛出InterruptedException异常,线程退出 TIMED_WAITING 状态,需由开发者处理该异常以决定后续执行逻辑。
方法签名:

方法归属与调用语义:
- public static void sleep(long millis) 与 public static void sleep(long millis,int nanos) 均被 static 关键词修饰了;
- sleep() 是 java.lang.Thread 类的静态方法,其调用行为严格绑定到当前执行该方法的线程(即调用上下文所在线程),而非任意线程对象:即使通过线程实例(如 t1.sleep(1000))调用,效果仍为当前执行该行代码的线程进入睡眠状态,而非目标线程实例 t1。
执行时序与线程状态转换:
- 调用该方法后,当前线程立即放弃 CPU 执行权,从 RUNNABLE(就绪 / 运行)状态切换至 TIMED_WAITING(计时等待)状态,在此期间不参与 CPU 的抢占式调度;
- 当指定时间阈值耗尽后,线程从 TIMED_WAITING 状态恢复为 RUNNABLE 状态,但需等待 CPU 调度器分配时间片后才能真正恢复执行,因此实际暂停时长可能略长于入参指定值,具体取决于 CPU 负载与调度策略,sleep() 的入参定义了线程暂停执行的最小时间阈值,而非精确执行时长。
同步锁持有特性:
若线程在持有同步锁的状态下调用 sleep(),该线程在睡眠期间不会释放已持有的任何同步锁资源:
- 对于 synchronized 隐式锁:线程持续持有对象监视器锁,其他线程无法竞争获取该锁,直至当前线程退出同步代码块 / 方法;
- 对于 ReentrantLock 等显式锁:线程持续持有锁,其他线程调用 lock() 会阻塞,直至当前线程显式调用 unlock() 释放锁。
中断响应与异常处理:
sleep() 是可中断方法,其行为遵循 Java 线程中断机制:
- 若当前线程处于 sleep() 引发的 TIMED_WAITING 状态时,其他线程调用该线程的 interrupt() 方法触发中断,当前线程会立即终止睡眠,在 sleep() 调用处抛出 java.lang.InterruptedException;
- 抛出该异常时,JVM 会自动清除当前线程的中断状态,可以通过 Thread.currentThread().isInterrupted() 验证;
- 因 InterruptedException 为检查型异常,调用 sleep() 时必须显式处理:要么通过 try-catch 块捕获并处理异常,要么在当前方法签名中通过 throws 关键字声明抛出该异常;
- 该异常是线程间协作的合法信号,用于实现线程间的中断协作,而非错误场景。
wait () 与 sleep (long timeout) 的核心区别
| 特性 | wait(long timeout) | sleep(long timeout) |
|---|---|---|
| 锁行为 | 调用时立即释放所持有的监视器锁 | 全程不释放任何锁(包括监视器锁、显式锁) |
| 唤醒机制 | 支持超时唤醒、notify/notifyAll | 主动唤醒、中断唤醒 仅支持超时唤醒、中断唤醒(无主动唤醒机制) |
| 恢复执行前提 | 需重新竞争并获取监视器锁后才能继续执行 | 无需竞争锁,超时 / 中断后直接继续执行 |
| 调用上下文 | 必须在 synchronized 同步环境中调用 | 可在任意上下文调用 |
| 所属类 | Object 类(所有对象均可调用) | Thread 类(仅线程对象 / 静态调用) |
sleep 与锁的关系代码示例
Thread.sleep() 的核心行为是放弃 CPU 执行权(线程进入 TIMED_WAITING 状态,暂时不参与 CPU 调度),这个行为与是否在 synchronized 代码块中无关。
情景一:非 synchronized 代码块中的 sleep
java
public static void main(String[] args) {
while(true){
System.out.println(new Date());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行逻辑:主线程执行 System.out.println() 打印当前时间;调用 sleep(1000):主线程立即让出 CPU 执行权,进入计时等待状态,CPU 可以去调度其他线程;1 秒后,主线程从 TIMED_WAITING 切换为 RUNNABLE(就绪)状态,等待 CPU 重新分配时间片;拿到 CPU 时间片后,主线程继续执行循环,再次打印时间。
如果没有 sleep(),这个死循环会瞬间占满一个 CPU 核心,而加了 sleep() 后,CPU 利用率会极低,这也是服务器端用 sleep() 的核心目的:避免空循环耗尽 CPU 资源。
情景二:synchronized 代码块中的 sleep
java
synchronized (ojb){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
执行逻辑:线程先竞争并获取 obj 的监视器锁,进入同步块;调用 sleep(1000),线程依然会让出 CPU 执行权,但始终持有 obj 的锁;这 1 秒内,CPU 可以调度其他非同步代码的线程运行,但任何需要获取 obj 锁的线程都会被阻塞(因为当前线程没释放锁);1 秒后,线程就绪并重新获取 CPU 时间片,执行完同步块后释放 obj 锁。
我们看到的其他线程拿不到锁无法执行同步代码的效果,这是锁未释放导致的,而非 CPU 没被让出,此时 CPU 完全可以去执行其他不需要 obj 锁的线程。
在独占锁(ReentrantLock)场景下 sleep 不会释放锁:
java
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SleepTest {
// 创建一个独占锁
private static final Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
//创建线程A
Thread threadA = new Thread(new Runnable() {
public void run() {
//获取独占锁
lock.lock();
try {
System.out.println("child threadA is in sleep");
Thread.sleep(1000);
System.out.println("child threadA is in awaked");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放锁
lock.unlock();
}
}
});
//创建线程B
Thread threadB = new Thread(new Runnable() {
public void run() {
//获取独占锁
lock.lock();
try {
System.out.println("child threadB is in sleep");
Thread.sleep(1000);
System.out.println("child threadB is in awaked");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放锁
lock.unlock();
}
}
});
//启动线程
threadA.start();
threadB.start();
}
}

如上代码首先创建了一个独占锁,然后创建了两个线程,每个线程在内部先获取锁,然后睡眠,睡眠结束后会释放锁。首先,无论执行多少次上面的代码都是线程A先输出或者线程B先输出,不会出现线程A和线程B交叉输出的情况。从执行结果来看,线程A先获取了锁,那么线程A会先输出一行,然后调用sleep 方法让自己睡眠1s,在线程A睡眠的这1s内那个独占锁lock还是线程A自己持有,线程B会一直阻塞直到线程A醒来后执行unlock释放锁。
ReentrantLock 是独占锁(排他锁),同一时刻只能有一个线程成功调用 lock() 获取锁,其他线程调用 lock() 时会进入阻塞状态,直到锁被释放。sleep 对显式锁(Lock)和隐式锁(synchronized)的表现相同,都不释放。
sleep 期间被中断抛出异常
java
public class SleepTest1 {
public static void main(String[] args) throws InterruptedException {
//创建线程A
Thread threadA = new Thread(new Runnable() {
public void run() {
try {
System.out.println("child threadA is in sleep");
Thread.sleep(5000);
System.out.println("child threadA is in awaked");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//启动线程
threadA.start();
Thread.sleep(2000);
threadA.interrupt();
}
}

线程 A 启动后,先打印 child threadA is in sleep,然后进入 sleep 状态,主线程等待 2 秒后,调用 threadA.interrupt() 中断线程 A,线程 A 的 sleep(5000) 被立即终止,抛出 InterruptedException,进入 catch 块打印异常堆栈,线程 A 不会执行 System.out.println("child threadA is in awaked"),因为 sleep 被中断后直接跳转到异常处理逻辑。
sleep 方法的应用场景
场景 1:控制线程执行节奏
通过 sleep () 让线程按预设频率执行任务,既满足业务对执行间隔的要求又避免线程高频抢占 CPU 资源,是 sleep () 最基础的应用场景。
典型场景:接口限流(控制线程每秒发起的外部接口请求数量)、定时轮询、模拟用户操作延迟。
注意事项:间隔时长需平衡业务时效性与资源消耗,过短仍会占用较多 CPU,过长则降低任务响应效率。
场景 2:优化空循环
当线程需循环等待某个条件满足(如等待文件生成、等待信号量触发、等待异步结果返回)时,空循环会导致 CPU 核心利用率接近 100%,sleep () 可通过阻塞线程大幅降低 CPU 消耗。
典型应用:循环等待文件生成、等待异步任务完成;
注意事项:避免在 synchronized/Lock 同步块内执行 "循环 + sleep",因 sleep () 不释放锁,会导致其他竞争锁的线程长时间阻塞。
场景 3:简单的线程时序协调
在低复杂度多线程场景中,通过 sleep () 实现线程间基础的时序控制,让一个线程临时等待另一个线程完成关键操作。
典型场景:主线程等待子线程完成初始化、让线程按指定顺序执行(临时方案)。
注意事项:
仅适用于临时、低精度的时序控制,复杂同步场景需优先使用 Object.wait ()、CountDownLatch、CyclicBarrier 等同步工具;
避免设置固定长时长 sleep,若目标线程提前完成,当前线程仍会等待 sleep 结束,造成资源浪费。
场景 4:调试 / 测试场景
在开发、测试阶段,通过 sleep () 模拟业务执行的耗时场景,复现并发环境下的线程安全、超时、资源竞争等问题。
典型应用:模拟接口调用延迟、复现并发问题;
注意事项:仅用于开发 / 测试阶段,生产环境严禁通过 sleep () 模拟业务耗时(会阻塞线程,降低服务并发能力)。
场景 5:后台守护线程
后台守护线程(如日志收集、系统心跳检测、临时文件清理)需持续运行但低频执行,sleep () 是降低其资源开销的核心工具。
典型应用:心跳检测线程、日志收集线程;
注意事项:守护线程需处理 InterruptedException,确保进程退出时线程能优雅终止。