Java常见技术分享-14-多线程安全-锁机制-常见的锁以及底层实现-synchronized

synchronized

核心特性
  • 可重入(同一线程可以多次获取同一个对象的锁, 不会因为自己已经持有的锁陷入阻塞)
  • 互斥 (同一时间内只有一个线程持有锁, 线程安全的核心)
  • 悲观 (假设线程的冲突概率高, 直接加锁, 悲观策略,让synchronized 在竞争激烈的情况下很稳定)
针对悲观策略 JDK1.6 之后的重大升级

在JDK1.6 之前 synchronized就是 纯悲观策略, 不管竞争是否激烈, 只要进入代码块, 直接加上重量级锁(阻塞与唤醒)。

JDK1.6之后, 就变成了根据竞争的激烈程度进行升级。

一开始是偏向锁, 只有一个线程的时候, 系统会在对象头记录 这个线程ID, 下次再进入同步代码块, 就不用申请锁了, 直接就能执行。

当有第二个线程 想要竞争锁的时候, 偏向锁 就会升级成轻量级锁, 即虚拟机 发现第二个线程要锁, 且对象头里记录的ID不是第二个线程的。

这时每个线程都会复制一份对象头 里的锁信息, 然后 双方都尝试用CAS操作 去 修改 对象头 里的 锁标记 成 自己的线程ID, 当修改成功 就拿到锁了。

具体的流程是, 在竞争之前 , 会先复制一份 锁标记的信息 , 然后在自己的存储区域 计算出 自己的锁记录地址 ,比如 0x456,更新 锁标记信息, 然后 对 对象头 里的锁标记 信息 执行CAS操作, 把自己的 新的 锁标记信息 更新到对象头上, 执行成功 就拿到锁了。

如果没成功, 就会自旋式的请求锁.(补充一个场景, 如果线程B拿到了锁, 锁标记中的锁记录地址是 0x456, 如果线程C进来 复制了 锁信息 ,是0x456, CAS操作, C虽然 确实 锁记录地址 与预期一致, 但是会因为线程B还在持有锁 而被阻止, 所以会失败, 知道线程B释放锁, 把 锁标记 重新改为默认的mark word副本)。

当同时 自旋的线程 或者 线程的自旋的次数 过多。 就会升级为重量级锁。

对象头里边 会存储一个 指向monitor对象的指针, 这个monitor对象就像一个锁管家 ,当线程竞争失败, 就会被monitor对象标记为阻塞状态,放入等待队列。 不会占用CPU资源。 需要注意的是 锁记录地址 转移到了monitor对象里边。

卧槽今天北京太冷了, 顶不住。 后面其实还有几个锁的,后面的篇章再说吧

相关推荐
starzy19901 分钟前
Scala循环与判断:一切皆表达式的控制流
开发语言·人工智能·scala
wangbing112511 分钟前
平台介绍-modbus设备调试
java
starzy199016 分钟前
Scala类和对象:从构造器到样例类的完整指南
开发语言·后端·scala
niucloud-admin27 分钟前
NIUCLOUD PHP SAAS上门家政使用文档--服务订单:订单列表
开发语言·php
西索斯coding35 分钟前
gpt-image-2.5-sunburst 和 gemini-3.1-flash-image 差多少?60 组项目素材的成功成本评测框架
java·linux·gpt·ai
怪我冷i1 小时前
Rust 的impl语法详解
开发语言·后端·rust
wuminyu1 小时前
Java字节码学习总结:字节码如何加载、解析和执行
java·linux·c语言·jvm·c++
qq_406981031 小时前
c++的decltype是什么
开发语言·c++
坤坤子吖1 小时前
算法学习——单调队列
开发语言·c++·笔记·学习·算法
Flynn Liang1 小时前
大模型怎么自己调函数?Spring AI 2.0 Tool Calling 自动循环原理 + 完整时序图
java·人工智能·spring·agent·函数调用·springai·toolcalling