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对象里边。

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

相关推荐
测试员周周3 小时前
【Appium 系列】第16节-WebView-H5上下文切换 — 混合应用的自动化难点
运维·开发语言·人工智能·功能测试·appium·自动化·测试用例
Mahir085 小时前
Spring 循环依赖深度解密:从问题本质到三级缓存源码级解析
java·后端·spring·缓存·面试·循环依赖·三级缓存
杜子不疼.6 小时前
【C++ AI 大模型接入 SDK】 - DeepSeek 模型接入(上)
开发语言·c++·chatgpt
加号36 小时前
【C#】 串口通信技术深度解析及实现
开发语言·c#
sycmancia6 小时前
Qt——编辑交互功能的实现
开发语言·qt
RyFit7 小时前
SpringAI 常见问题及解决方案大全
java·ai
石山代码7 小时前
C++ 内存分区 堆区
java·开发语言·c++
绝知此事7 小时前
【算法突围 01】线性结构与哈希表:后端开发的收纳术
java·数据结构·算法·面试·jdk·散列表
无风听海7 小时前
C# 隐式转换深度解析
java·开发语言·c#
一只大袋鼠8 小时前
Git 进阶(二):分支管理、暂存栈、远程仓库与多人协作
java·开发语言·git