synchronized在异常发生时自动释放锁,由JVM通过monitorenter和两个monitorexit指令保障,无论RuntimeException、Error、Checked Exception或InterruptedException,均确保锁释放及内存可见性。

synchronized 在异常发生时会自动释放锁,不需要手动干预,这是它最稳定、最省心的特性之一。
所有异常都会触发锁释放
不管抛出的是哪种异常,JVM 都会确保锁被释放:
- RuntimeException 及其子类(如 NullPointerException、ArrayIndexOutOfBoundsException)
- Error 类型(如 OutOfMemoryError、StackOverflowError),哪怕程序已处于严重故障状态
- 未捕获的受检异常(Checked Exception),只要它逃出 synchronized 块边界
- 线程中断引发的 InterruptedException,即使没写 catch 或 finally
底层靠字节码指令保障
编译后,synchronized 代码块会插入 monitorenter 和两个 monitorexit 指令:
- 一个用于正常执行结束路径
- 另一个绑定在异常表(exception table)中,覆盖所有可能的异常出口
- 这意味着:不依赖 try-catch,不依赖 return 语句,也不怕 JVM 突然崩溃——只要同步块开始执行过,退出时就一定释放锁
和 ReentrantLock 的关键区别
synchronized 的释放是 JVM 层面的硬性规则,而 Lock 是纯 API 调用:
立即学习“Java免费学习笔记(深入)”;
- synchronized:锁释放由 monitor 状态机驱动,不可绕过,无需 finally
- ReentrantLock:unlock() 是普通方法,漏掉或放在 catch 里但没执行,就会死锁
- 也就是说,synchronized 把“必须释放”这件事从开发者责任变成了 JVM 责任
释放时仍保证内存可见性
异常中途退出,不影响 Java 内存模型的语义:
- 锁释放前,线程会把本地工作内存中修改的共享变量刷新回主内存
- 下一个获得该锁的线程,会强制从主内存重新读取最新值
- 所以即使方法因异常提前结束,之前已完成的赋值操作依然对其他线程可见


















