- 成功,则解锁成功
- 失败,说明轻量级锁进行了锁膨胀或已经升级为重量级锁,进入重量级锁解锁流程
文章插图
锁膨胀我们首先简单介绍一下锁膨胀:
- 如果在尝试加轻量级锁的过程中,CAS 操作无法成功,这时一种情况就是有其它线程为此对象加上了轻量级锁(有竞争)
- 这时需要进行锁膨胀,将轻量级锁变为重量级锁 。
- 当 Thread-1 进行轻量级加锁时,Thread-0 已经对该对象加了轻量级锁
文章插图
- 这时 Thread-1 加轻量级锁失败,进入锁膨胀流程
- 即为 Object 对象申请 Monitor 锁,让 Object 指向重量级锁地址,然后自己进入 Monitor 的 EntryList BLOCKED
文章插图
- 当 Thread-0 退出同步块解锁时 , 使用 cas 将 Mark Word 的值恢复给对象头,失败 。
- 这时会进入重量级解锁 流程,即按照 Monitor 地址找到 Monitor 对象,设置 Owner 为 null,唤醒 EntryList 中 BLOCKED 线程
- 重量级锁竞争的时候,还可以使用自旋来进行优化
- 如果当前线程自旋成功(即这时候持锁线程已经退出了同步块 , 释放了锁),这时当前线程就可以避免阻塞 。
线程1 (core 1上)对象Mark线程2 ( core 2上)-10(重量锁)-访问同步块,获取monitor10(重量锁)重量锁指针-成功(加锁)10(重量锁)重量锁指针-执行同步块10(重量锁)重量锁指针-执行同步块10(重量锁)重量锁指针访问同步块,获取 monitor执行同步块10(重量锁)重量锁指针自旋重试执行完毕10(重量锁)重量锁指针自旋重试成功(解锁)01(无锁)自旋重试-10(重量锁)重量锁指针成功(加锁)-10(重量锁)重量锁指针执行同步块-......我们也给出自旋失败的案例:
线程1 ( core 1上)对象Mark线程2( core 2上)-10(重量锁)-访问同步块,获取monitor10(重量锁)重量锁指针-成功(加锁)10(重量锁)重量锁指针-执行同步块10(重量锁)重量锁指针-执行同步块10(重量锁)重量锁指针访问同步块 , 获取monitor执行同步块10(重量锁)重量锁指针自旋重试执行同步块10(重量锁)重量锁指针自旋重试执行同步块10(重量锁)重量锁指针自旋重试执行同步块10(重量锁)重量锁指针阻塞-......我们针对自旋添加一些注意信息:
- Java 7 之后不能控制是否开启自旋功能
- 自旋会占用 CPU 时间,单核 CPU 自旋就是浪费,多核 CPU 自旋才能发挥优势 。
- 在 Java 6 之后自旋锁是自适应的,比如对象刚刚的一次自旋操作成功过 , 那么认为这次自旋成功的可能性会高,就多自旋几次
- 在 Java 6 之后自旋锁是自适应的,比如对象刚刚的一次自旋操作失败,那么就认为这次自旋成功的可能性会低,少自旋甚至不自旋
- 轻量级锁在没有竞争时(就自己这个线程) , 每次重入仍然需要执行 CAS 操作 。
- 偏向锁:只有第一次使用 CAS 将线程 ID 设置到对象的 Mark Word 头,之后发现 这个线程ID是自己的表示没有竞争,不用重新 CAS 。
- 以后只要不发生竞争,这个对象就归该线程所有
// 下述三个操作完全不具有时间冲突 , 相当于全由m1来管辖 , 这时m1使用会进行cas更换,后续m2,m3均不会进行cas切换static final Object obj = new Object();public static void m1() {synchronized( obj ) {// 同步块 Am2();}}public static void m2() {synchronized( obj ) {// 同步块 Bm3();}}public static void m3() {synchronized( obj ) {// 同步块 C}}
我们给出简单示例图:graph LRsubgraph 偏向锁t5("m1内调用synchronized(obj)")t6("m2内调用synchronized(obj)")t7("m2内调用synchronized(obj)")t8(对象)t5 -.用ThreadID替换MarkWord.-> t8t6 -.检查ThreadID是否是自己.-> t8t7 -.检查ThreadID是否是自己.-> t8endsubgraph 轻量级锁t1("m1内调用synchronized(obj)")t2("m2内调用synchronized(obj)")t3("m2内调用synchronized(obj)")t1 -.生成锁记录.-> t1t2 -.生成锁记录.-> t2t3 -.生成锁记录.-> t3t4(对象)t1 -.用锁记录替换markword.-> t4t2 -.用锁记录替换markword.-> t4t3 -.用锁记录替换markword.-> t4end偏向状态
推荐阅读
- Seata Server 1.5.2 源码学习
- 2022极端高温!机器学习如何预测森林火灾?? 万物AI
- 1.nginx学习
- 常用Python库整理
- 图学习参考资料 词向量word2vec
- 五 RK3568开发笔记:在虚拟机上使用SDK编译制作uboot、kernel和ubuntu镜像
- realme笔记本最新消息_realme笔记本即将发布
- JUC学习笔记——进程与线程
- Seata 1.5.2 源码学习
- 第4版 高性能MySQL 第一章 MySQL架构 读书笔记