线程睡眠sleep方法

线程睡眠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,确保进程退出时线程能优雅终止。

相关推荐
Scott9999HH6 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔7 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社7 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海8 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖8 小时前
JDK 26 新特性详解
java·开发语言
马优晨9 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区11 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大11 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai12 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说12 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json