synchronized在未捕获异常时自动释放锁,因jvm编译时生成两个monitorexit指令:一个用于正常退出,一个嵌入异常表覆盖所有异常路径,确保无论runtimeexception、error还是未捕获受检异常,均触发计数器减1并清空_owner;该机制不依赖finally,也不受中断影响,是jvm硬性保障,而lock需手动unlock且易因遗漏导致死锁。

synchronized 在遇到未捕获异常(UncaughtException)时,会自动释放锁,这是 JVM 的强制保障机制,无需开发者干预。
为什么能自动释放?
Java 虚拟机在编译 synchronized 代码块时,会生成两个 monitorexit 指令:
- 一个用于正常执行结束路径
- 另一个专门插入在异常处理表(exception table)中,覆盖所有可能抛出异常的字节码范围
只要线程因未捕获异常退出同步块,JVM 就会触发对应的 monitorexit 指令,将 Monitor 对象内部的计数器减 1;当计数器归零,锁即完全释放,_owner 字段被清空。
哪些异常会触发自动释放?
所有未被 try-catch 捕获的异常都会触发,包括:
- RuntimeException 及其子类(如 NullPointerException、ArrayIndexOutOfBoundsException)
- Error(如 OutOfMemoryError、StackOverflowError)——注意:虽属严重错误,JVM 仍会执行 monitorexit
- 任何未显式捕获的受检异常(Checked Exception),只要它逃逸出 synchronized 块边界
不依赖 finally,也不怕中断
与 Lock 接口不同,synchronized 的释放不依赖于代码逻辑是否走到 finally 块:
- 即使同步块里没有写 catch,也没有 finally,照样释放
- 即使线程被 Thread.interrupt() 中断并抛出 InterruptedException,只要该异常未被捕获,锁仍释放
- 虚拟线程环境下(Java 24+),即便因锁竞争而挂起,异常退出后平台线程也会被及时解绑
对比 Lock 的关键差异
Lock 必须手动调用 unlock(),且必须放在 finally 中;而 synchronized 的释放是 JVM 层面的硬性保证:
- synchronized:释放由字节码和 Monitor 状态机驱动,不可绕过
- Lock:unlock() 是普通方法调用,忘记调用或异常发生在 unlock() 之前,就会导致死锁
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











