synchronized锁的释放完全自动,无需手动干预:正常执行结束、遇到return语句或发生未捕获异常时,JVM均通过monitorexit指令自动释放锁,且在所有异常出口处插入该指令以保障可靠性。

synchronized 锁的释放是自动完成的,不需要手动调用任何方法。只要线程执行完 synchronized 修饰的代码块或方法,JVM 就会在字节码层面插入 monitorexit 指令,触发锁的释放。
锁自动释放的三种典型场景
无论哪种写法,只要控制流正常离开同步区域,或因异常退出,锁都会被释放:
- 正常执行结束:线程执行完 synchronized 方法体或代码块最后一行,自动释放锁。
- 遇到 return 语句:即使在同步块中间 return,JVM 仍会保证先执行锁释放逻辑,再跳出方法。
- 发生未捕获异常:比如 NullPointerException 或 ArithmeticException,线程抛出异常并退出同步区域时,锁同样会被自动释放——这是 synchronized 的关键保障之一,避免死锁。
为什么能“自动”?底层靠的是字节码契约
JVM 规范强制要求:每个 monitorenter(加锁)指令,必须对应至少一个 monitorexit(解锁)指令。编译器会在以下位置自动生成 monitorexit:
- 方法末尾(对应 synchronized 方法)
- 代码块末尾(对应 synchronized 块)
- 所有可能的异常出口(包括 try/catch 之外的异常路径)
这种机制不依赖程序员干预,也不受 finally 块影响——它发生在 JVM 执行引擎层面,比 Java 代码更底层。
立即学习“Java免费学习笔记(深入)”;
和显式锁(如 ReentrantLock)的关键区别
ReentrantLock 必须配对调用 lock() 和 unlock(),且通常要放在 finally 块中防止漏释放;而 synchronized 没有 unlock 调用,也不会因忘记释放导致资源泄露。这种“自动性”正是它简单可靠的核心原因。
注意:wait() 不等于释放锁的终点
调用 object.wait() 会主动释放当前持有的锁,并进入 WaitSet 等待唤醒;但此时不是“锁释放完成”,而是“临时让出”。当线程被 notify/notifyAll 唤醒、重新竞争到锁后,才会继续执行 wait() 后面的代码。这个过程仍是自动管理的,无需手动 re-lock。


















