解读synchronized锁的释放机制

 更新时间:2025年04月17日 16:44:15   作者:堕落年代  
这篇文章主要介绍了synchronized锁的释放机制,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

synchronized锁的释放机制

synchronized 锁的释放机制是通过 JVM 底层的 Monitor 锁模型计数器机制 实现的。

以下是具体的释放逻辑和触发条件:

一、锁释放的触发条件

正常执行结束

当线程执行完 synchronized 修饰的代码块或方法时,JVM 会自动调用 monitorexit 指令释放锁。

  • 同步代码块:通过 monitorentermonitorexit 指令显式控制锁的获取与释放。
  • 同步方法:通过 ACC_SYNCHRONIZED 标志隐式触发锁的获取和释放,方法结束时自动释放锁。

异常退出

  • 如果在 synchronized 代码块或方法中抛出未捕获的异常,JVM 会执行 monitorexit 指令释放锁。
  • 编译时会为同步代码块生成两个 monitorexit 指令:一个用于正常退出,另一个用于异常路径。

显式调用 wait()

  • 线程在 synchronized 代码块内调用 wait() 方法时
  • 会暂时释放锁并进入等待状态,直到其他线程调用 notify()/notifyAll() 唤醒它

二、锁释放的核心机制

计数器递减

  • 每个对象关联一个 Monitor 锁,内部维护一个计数器(_count)。
  • 线程每次进入 synchronized 代码块时计数器加 1,退出时减 1。
  • 当计数器归零时,锁完全释放,其他线程可竞争获取。

Monitor 对象的状态管理

  • 持有锁的线程(_owner:释放锁后,_owner 置为 null,计数器归零。
  • 等待队列(_EntryList_WaitSet:锁释放后,JVM 会从 _EntryList_WaitSet 中唤醒线程重新竞争锁。

三、不同场景的锁释放示例

1. 同步代码块

public void method() {
    synchronized (this) {
        // 代码逻辑
    } // 此处自动执行 monitorexit 释放锁
}

无论正常结束还是抛出异常,monitorexit 都会触发锁释放。

2. 同步方法

public synchronized void method() {
    // 代码逻辑
} // 方法结束自动释放锁

通过 ACC_SYNCHRONIZED 标志隐式管理锁,无需显式字节码指令。

3. 异常场景

public void method() {
    synchronized (this) {
        throw new RuntimeException(); // 触发异常,自动释放锁
    }
}

即使未捕获异常,JVM 也会执行 monitorexit 指令释放锁。

四、锁释放的底层实现(字节码层面)

同步代码块

编译后生成 monitorenter 和两个 monitorexit(正常退出和异常退出)指令:

public void method();
  Code:
     0: aload_0
     1: dup
     2: astore_1
     3: monitorenter     // 获取锁
     4: ...              // 业务代码
    13: monitorexit      // 正常退出释放锁
    14: goto 20
    17: aload_1
    18: monitorexit      // 异常退出释放锁
    19: athrow
    20: return

同步方法

方法访问标志包含 ACC_SYNCHRONIZED,JVM 在方法入口和出口隐式管理锁。

五、注意事项

不会释放锁的操作

  • Thread.sleep()Thread.yield() 不会释放锁。
  •  线程挂起(如 suspend())也不会释放锁。

可重入性

  • 同一线程可多次获取锁(计数器递增)
  • 需对应次数的退出操作才能完全释放

总结

synchronized 锁的释放依赖于 JVM 的 Monitor 模型和计数器机制,通过以下方式触发:

  1. 代码块或方法正常结束。
  2. 未捕获异常抛出。
  3. 显式调用 wait()
  4. 其底层通过 monitorexit 指令或隐式标志确保锁的正确释放,保障线程安全。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java,C#使用二进制序列化、反序列化操作数据

    Java,C#使用二进制序列化、反序列化操作数据

    这篇文章主要介绍了Java,C#使用二进制序列化、反序列化操作数据的相关资料,需要的朋友可以参考下
    2014-10-10
  • SpringBoot整合mysql、postgres及sqlserver实现多数据源配置实战案例

    SpringBoot整合mysql、postgres及sqlserver实现多数据源配置实战案例

    在工作中业务的发展或业务数据隔离的场景下,通常需要一个项目中引入多个数据源,但SpringBoot默认的自动化配置是单数据源的,这篇文章主要给大家介绍了关于SpringBoot整合mysql、postgres及sqlserver实现多数据源配置的相关资料,需要的朋友可以参考下
    2023-12-12
  • MybatisPlus LambdaQueryWrapper使用int默认值的坑及解决

    MybatisPlus LambdaQueryWrapper使用int默认值的坑及解决

    这篇文章主要介绍了MybatisPlus LambdaQueryWrapper使用int默认值的坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教。
    2022-01-01
  • 简单介绍一下什么是microservice微服务

    简单介绍一下什么是microservice微服务

    这篇文章主要介绍了一下什么是microservice微服务微服务的定义,微服务到底是什么意思?什么样的架构可以叫做微服务?这篇文章可以给你答案
    2023-03-03
  • SpringBoot操作Mongodb的实现示例

    SpringBoot操作Mongodb的实现示例

    本文主要介绍了SpringBoot操作Mongodb的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • SpringBoot-Maven打包压缩瘦身方式

    SpringBoot-Maven打包压缩瘦身方式

    这篇文章主要介绍了SpringBoot-Maven打包压缩瘦身方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-05-05
  • SpringBoot扩展SpringMVC原理并实现全面接管

    SpringBoot扩展SpringMVC原理并实现全面接管

    这篇文章主要介绍了SpringBoot扩展SpringMVC原理并实现全面接管,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • jdk线程池的实现

    jdk线程池的实现

    本文主要介绍了jdk线程池的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • 解决nacos项目启动报错:Connection refused: no further informa问题

    解决nacos项目启动报错:Connection refused: no further&

    这篇文章主要介绍了解决nacos项目启动报错:Connection refused: no further informa问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • 关于ArrayList初始化容量的问题

    关于ArrayList初始化容量的问题

    这篇文章主要介绍了关于ArrayList初始化容量的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03

最新评论