后端原理实验室

一把锁,两条线程 · 交互样章

线程走到哪一步了?

看状态,也看它为什么停下。先点图,再跟着 A、B 走一遍。

01 / 六个状态,一张路线图

点一个节点,查看进出规则

箭头是常见路径的概念图,不是全部 API 转移。WAITING / TIMED_WAITING 的返回路径取决于操作;Object.wait 必须先重获 monitor,sleep 不涉及释放锁。RUNNABLE 包含等待 CPU。

02 / A 与 B,谁能进门?

一条合法示例路径 · Java 8+ 语义 · 非真实 JVM 执行。自动播放约 2 分 16 秒,预测题会停下等你。

A
同一个 monitor · M
B

    可回看已揭晓的步骤;往前跳仍会先遇到预测。预测用于理解,不计入基线测评、不保存分数。

    代码与分镜同步

    示意片段:M 是共享 final 对象,ready 初始为 false;略去类与业务方法

    线程 A

    线程 B

    ready 的读写都在同一把 M 下。doOneBoundedChunk() 代表有限时长、能返回的工作;如果它永久阻塞,循环检查本身无法保证及时取消。

    03 / 用短视频,再串一次

    中文字幕 · 无旁白
    文字稿 / 不播放动画也能读完

    Java 线程生命周期:一种合法的示例顺序

    84 秒,无声,中文字幕。A 为青绿,B 为琥珀色;中央显示对象 m 的 monitor owner。 所有画面只表示一种合法顺序,不承诺或预测 JVM 的调度。

    [00:00:00.000 – 00:00:03.500] Java 线程有六种状态;任一时刻,一个线程只处于其中一种。

    [00:00:03.500 – 00:00:07.000] 下面跟随线程 A、B 和对象 m,观察一种合法的执行顺序。

    [00:00:07.000 – 00:00:11.000] 创建线程后是 NEW。调用 start(),线程进入 RUNNABLE。

    [00:00:11.000 – 00:00:15.000] RUNNABLE 不等于一直占用 CPU;等待处理器也可能是这个状态。

    [00:00:15.000 – 00:00:19.000] 本例 A 先获得 m 的 monitor,进入 synchronized(m)。

    [00:00:19.000 – 00:00:23.000] B 尝试获取同一个 monitor,但 A 尚未释放,B 进入 BLOCKED。

    [00:00:23.000 – 00:00:27.000] A 在同步块里调用 sleep(),进入 TIMED_WAITING。

    [00:00:27.000 – 00:00:31.000] sleep() 不释放 monitor,所以 owner 仍是 A,B 仍然 BLOCKED。

    [00:00:31.000 – 00:00:35.000] 睡眠结束后,A 继续执行;条件不满足,于是在循环内调用 m.wait()。

    [00:00:35.000 – 00:00:38.000] A 进入 WAITING,并释放 m 的 monitor。本例 B 随后获得它。

    [00:00:38.000 – 00:00:41.000] wait() 只释放 m 的 monitor,不会释放 A 持有的其他对象的锁。

    [00:00:41.000 – 00:00:45.000] B 将条件 ready 设为 true,然后调用 m.notify(),本例唤醒 A。

    [00:00:45.000 – 00:00:50.000] notify() 不释放锁。A 离开等待集,但仍因重新获取 monitor 而 BLOCKED。

    [00:00:50.000 – 00:00:54.000] B 退出同步块,释放 monitor。本例 A 随后重新获得它。

    [00:00:54.000 – 00:00:59.000] wait() 返回后,A 必须在 while 循环中重新检查条件,防止虚假唤醒等情况。

    [00:00:59.000 – 00:01:05.000] 条件满足,A 完成工作、退出同步块并结束 run(),进入 TERMINATED。

    [00:01:05.000 – 00:01:10.000] 另一个线程向 B 发送 interrupt()。本例 B 仍是 RUNNABLE,只是中断标志被设置。

    [00:01:10.000 – 00:01:17.000] B 的代码主动检查中断标志,完成清理并退出 run(),随后才进入 TERMINATED。

    [00:01:17.000 – 00:01:24.000] 中断不会自动终止线程。实际调度顺序可能不同,要分别观察线程状态和锁的 owner。

    技术补充 1. BLOCKED 特指等待 monitor 锁,不泛指所有形式的阻塞。 2. wait/notify 都须在拥有对应对象 monitor 时调用。wait 只释放对应对象的 monitor。 3. notify 唤醒等待集中的一个线程,选择不确定;本示例只有 A 在等待。被通知线程须再次竞争 monitor,无优先拿锁保证。 4. wait 必须置于条件循环中。本例 B 在持有 m 时写 ready;A 重新获得同一 monitor 后检查 ready。 5. sleep、带正数超时的 wait/join 等可对应 TIMED_WAITING;无超时的 wait/join 和 park 等可对应 WAITING。视频不展开所有状态转换。 6. interrupt 的具体效果依赖线程正在做什么。示例 B 在同步块外运行,通过 isInterrupted() 检查标志并协作式退出。wait/sleep/join 也可能通过 InterruptedException 响应,抛出该异常时中断状态被清除。 7. run 正常返回或异常结束均可能导致线程终止;本例展示正常结束。

    权威来源(核对日期:2026-10-08) https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/Thread.State.html https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/Thread.html#sleep(long) https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/Thread.html#interrupt() https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/Object.html#wait(long,int) https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/Object.html#notify()

    想再往下挖?

    中断与等待:这段故事没有展开的路径

    线程在 sleep、wait 或 join 中被中断,通常以 InterruptedException 响应并清除中断标记。本例 A 的 catch 恢复中断标记后退出。

    若正在 Object.wait,抛出 InterruptedException 前也要重新获得调用对象的 monitor。普通 synchronized 入口竞争本身不响应 interrupt 退出;不要把中断画成一条“任何状态 → 立即结束”的箭头。

    notify 任意选择一个等待者;notifyAll 唤醒所有等待者,但它们仍要逐个竞争 monitor。生产代码需要根据条件协议选择通知方式。

    展开原教材:状态、等待、中断与边界

    回到原课程的完整实验与练习 → · 手册阅读标记与官方参考 →

    只看静态分镜(适合减少动态效果)

      核对规则:Thread.State · Object.wait / notify · Thread.interrupt