文章目录

Java Thread.sleep与Object.wait的锁释放行为差异

发布于 2026-05-06 21:15:51 · 浏览 11 次 · 评论 0 条

Java Thread.sleep与Object.wait的锁释放行为差异

Java 多线程编程中,控制线程暂停的两种最常见方式是 Thread.sleepObject.wait。虽然它们都能让线程停止运行,但在锁的释放行为上有着本质的区别。理解这一差异对于避免死锁和提高并发性能至关重要。


1. 验证 Thread.sleep 的锁持有行为

Thread.sleep 会让当前线程休眠指定时间,但不会释放它持有的任何锁。

创建一个名为 SleepDemo 的 Java 类,并输入以下代码:

public class SleepDemo {
    private static final Object lock = new Object();

    public static void main(String[] args) {
        Thread threadA = new Thread(() -> {
            synchronized (lock) {
                System.out.println("线程A:获取锁,开始睡觉");
                try {
                    // 休眠 5 秒
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程A:睡醒了,释放锁");
            }
        });

        Thread threadB = new Thread(() -> {
            synchronized (lock) {
                System.out.println("线程B:获取到锁了");
            }
        });

        threadA.start();
        // 确保 A 先启动
        try { Thread.sleep(100); } catch (InterruptedException e) {}
        threadB.start();
    }
}

运行程序,观察控制台输出。你会发现“线程B:获取到锁了”这句话会在 5 秒后才打印出来。这证明线程 A 在睡眠期间依然占有着 lock 对象,线程 B 只能阻塞等待。


2. 验证 Object.wait 的锁释放行为

Object.wait 必须在 synchronized 块或方法中调用。与 sleep 不同,wait 方法会释放当前线程持有的对象锁,允许其他线程进入同步块。

创建一个名为 WaitDemo 的 Java 类,并输入以下代码:

public class WaitDemo {
    private static final Object lock = new Object();

    public static void main(String[] args) {
        Thread threadA = new Thread(() -> {
            synchronized (lock) {
                System.out.println("线程A:获取锁,开始等待");
                try {
                    // 等待 5 秒或被唤醒,此处会释放锁
                    lock.wait(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程A:等待结束,重新拿到锁并执行");
            }
        });

        Thread threadB = new Thread(() -> {
            synchronized (lock) {
                System.out.println("线程B:获取到锁了");
            }
        });

        threadA.start();
        try { Thread.sleep(100); } catch (InterruptedException e) {}
        threadB.start();
    }
}

运行程序。你会发现“线程B:获取到锁了”这句话几乎是立即打印的。这证明线程 A 调用 wait 后立刻释放了锁,线程 B 随即进入了同步块。


3. 核心差异对比表

以下表格总结了两者在锁处理上的关键区别:

特性 Thread.sleep Object.wait
锁行为 不释放锁 释放锁
所属类 java.lang.Thread java.lang.Object
使用位置 任何地方 必须在 synchronized 代码块或方法内
唤醒方式 时间结束、interrupt notify/notifyAll、时间结束、interrupt
恢复执行 时间到了自动恢复 需重新竞争获取锁

4. 执行流程可视化

为了更直观地理解两者在锁状态上的流转,请参考以下流程图:

graph TD subgraph "Thread.sleep 流程" A1["线程进入 synchronized 块"] --> B1["调用 Thread.sleep()"] B1 --> C1["线程休眠 (保持运行状态)"] C1 --> D1["持有对象锁"] D1 --> E1["其他线程尝试获取锁 -> 阻塞"] E1 --> F1["休眠时间结束"] F1 --> G1["继续执行代码"] end subgraph "Object.wait 流程" A2["线程进入 synchronized 块"] --> B2["调用 lock.wait()"] B2 --> C2["线程进入等待集"] C2 --> D2["释放对象锁"] D2 --> E2["其他线程可获取锁并执行"] E2 --> F2["收到 notify 或超时"] F2 --> G2["重新竞争锁"] G2 --> H2["拿到锁后继续执行"] end

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文