thread.sleep()在synchronized块内不释放锁,锁仅在同步块正常结束、return/break跳出、抛出未捕获异常或显式调用wait()时释放;sleep仅使线程进入timed_waiting状态,不影响锁持有。

Thread.sleep() 在 synchronized 块内执行时,锁不会释放——这是 Java 多线程中一条必须守住的硬性规则。它不是“暂停就让位”,而是“睡着了也攥着钥匙”。只要没退出 synchronized 作用域,锁就始终被当前线程独占,其他线程只能阻塞等待。
synchronized 什么时候会真正释放锁?
锁的释放只发生在明确的退出点,和线程是否休眠无关:
- 同步代码块或同步方法正常执行结束
- 遇到
return或break提前跳出 - 抛出未捕获的异常(
Exception或Error) - 显式调用
wait()—— 这是唯一在同步块内能主动释放锁的操作
而 Thread.sleep()、Thread.yield()、Thread.suspend()(已废弃)这些方法,都只是让线程状态改变,不触发锁释放逻辑。
为什么 sleep 不放锁?本质是“挂起 ≠ 让权”
sleep() 的作用是把线程置为 TIMED_WAITING 状态,交出 CPU 执行权,但它不参与锁的生命周期管理:
- 它不申请锁、不释放锁、也不参与锁竞争
- synchronized 锁(即 monitor)由 JVM 在进入时获取、退出时自动释放,中间任何暂停都不影响其持有状态
- 哪怕睡 10 秒,只要还在 synchronized 块里,其他线程就进不了同一把锁保护的临界区
典型错误写法与后果
以下代码看似无害,实则埋下性能隐患:
synchronized (lock) {
doCriticalWork();
Thread.sleep(2000); // ❌ 锁全程被占,别人干等两秒
logResult(); // 非关键操作也卡在锁里
}
后果包括:
- 吞吐量骤降:多个线程排队等同一把锁
- 响应延迟升高:本可并发的操作被迫串行
- 死锁风险上升:若该锁又与其他锁交叉持有,容易形成循环等待
安全替代方案
要实现“延时 + 释放锁”,不能靠 sleep(),得换思路:
-
缩小锁范围:只把真正需要互斥的代码包进
synchronized,把sleep()、日志、I/O 等移出来 -
改用
wait():需配合notify()使用,它会在等待前自动释放锁,唤醒后重新争抢 -
用
ScheduledExecutorService:适合定时任务,完全脱离锁上下文,避免耦合
口诀记牢:sleep 是打盹,不是交钥匙;锁还在你手上,别人进不来。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











