【从刷面试题到构建知识体系】Java底层-synchronized锁-2偏向锁篇
上一篇通过构建金字塔结构,来从不同的角度,由浅入深的对synchronized关键字做了介绍,
快速跳转:https://www.cnblogs.com/xyang/p/11631866.html
本文将从底层实现的各个“组件”着手,详细拆解其工作原理。
本文会分为以下4节内容:
第一节:介绍MarkWord和LockRecord两种数据结构,该知识点是理解synchronized关键字底层原理的关键。
第二节:分析偏向锁加锁解锁时机和过程
一.先来了解两种数据结构,你应该了解这些知识点
1.MarkWord:在锁的使用过程中会对锁对象作出相应的操作
在HotSpot虚拟机中,Java对象在内存中存储的布局,分为三个部分:对象头,实例数据,对齐填充。
本文重点关注对象头。
对象头又划分为2或3部分,具体包括:
- MarkWord(后文简称MW,后续详细介绍)
- 类型指针:指向这个对象所属的类的元数据(klass)的指针
- 最后这一部分比较特殊,只有在对象是Java数组时才会存在,记录的是数组的长度。为什么要存在这个记录呢?我们知道,在普通Java对象中,我们可以通过读取对象所属类的元数据,计算出对象的大小。而数组是无法做到的,于是借助这块区域来记录。
本文重点关注MW区域
MW是一块固定大小内存区域,在32位虚拟机中是32个bit,对应的,64位虚拟机中是64个bit。本文以32位虚拟机为例分析。
我们从直观上理解,所谓的头信息,一般都是用来记载一些不易变的信息,例如在http请求头中的各种头信息。在对象头中也是如此,例如hashcode。在JVM虚拟机中为了解决存储空间开销,对象头的MW大小已经固定。那么,要存储的信息有比较多,包括且不限于:锁标志位、GC信息、锁相关信息,总大小远远超出32bit,怎么办呢?
共享存储区域,在不同的时刻,根据需求存储需要的信息。
请参考下图:
锁类型 |
25bit |
4bit |
1bit |
2bit |
|
---|---|---|---|---|---|
23bit |
2bit |
是否偏向锁 |
锁标志位 |
||
无锁 |
对象hashcode |
分代年龄 |
0 |
01 |
|
偏向锁 |
线程ID |
epoch |
分代年龄 |
1 |
01 |
轻量级锁 |
指向栈中锁记录的指针 |
00 |
|||
重量级锁 |
指向互斥量 |
10 |
|||
GC标记 |
空 |
11 |
说明:两个标志位最多只能标识4个状态,那么剩下一个怎么办?共享。无锁和偏向锁共享01状态,他们两个的区分
2.LockRecord:
在当前线程的栈中申请LR(LockRecord简称,下同),主要包含两部分,第一步部分可以用于存放MW的副本;第二部分obj,用于指向锁对象。
上述两者的关系用下图表示:
二.偏向锁怎么工作
在对象创建的时候,MW会有一个初始态,要么是无锁态,要么是初始偏向锁态(ThreadId、epoch值都为初始值0)。程序员的世界不存在二义性,最终总会选一个,选择的依据是虚拟机的配置参数,在JDK1.6以后,默认是开启的,如果要禁用掉:-XX:-UseBiasedLocking。
什么时候需要禁用呢?如果能确认程序在大多数情况下,都存在多线程竞争,那么就可以禁用掉偏向锁。没必要每次都走一遍偏向锁->轻量级锁->重量级锁的完整升级流程。
1.基本流程
2.加锁过程
步骤一:
- LR记录赋值:在当前线程的栈中,申请一个LR,把obj指向锁对象
步骤二:如图中所示,线程T1,执行到同步代码,尝试加偏向锁,首先会做【偏向锁是否可用】的判断:
- 锁对象的对象头MW区域后3个bit位的值是101。特别需要注意:如果是001,是无锁状态,代表偏向锁不可用,会走加轻量级锁流程。
- ThreadId值:
- 如果ThreadId=0,代表无任何线程持有该对象的偏向锁,可以执行加锁操作,进入加锁流程;
- 如果ThreadId!=0,就判断其值是否是当前线程的ID,分两种情况:如果是,直接锁重入,不再重复加锁。如果否,说明是其他线程(图中T2)已获得了同步锁,进入“第三步”锁竞争流程。
- epoch值:对象所属Class里也会维护一个epoch值,这里我们简称为cEpoch,对该值的判断,可能会导致两种操作:
- 如果epoch<cEpoch,且ThreadId!=0,说明发生过批量重偏向,当前锁对象已被“释放”了。此时进行“重偏向”(里说的释放并非真正意义的释放,而是隐含着一层意思:当前线程已经执行完同步块,且在某次重偏向操作中,也检测到这一点,不再维护epoch的最新值,这样新的线程认为此时该偏向锁,可以加锁,直接CAS修改ThreadId即可)。
- 如果ThreadId==0,因为偏向锁没有显示的撤销修改ThreadId过程,说明肯定是初始状态,那么epoch值也肯定是初始状态0,此时直接进行加锁操作。
可加锁状态的MW内容如下图所示:
锁类型 |
25bit |
4bit |
1bit |
2bit |
|
23bit |
2bit |
是否偏向锁 |
锁标志位 |
||
偏向锁 |
ThreadId==0 |
epoch==n |
分代年龄 |
1 |
01 |
以上三个点都判断通过,进入“第二步”,加锁流程
第二步:通过CAS原子操作,把T1的ThreadId写入MW。执行结果有两种情况:
- 写入成功,获得偏向锁,进入同步代码块执行同步逻辑。
- 写入失败,表明在第一步判断和CAS操作之间,有其他线程已获得了锁。走锁竞争逻辑。
2.解锁过程
当前线程执行完同步代码块后,进行解锁,解锁操作比较简单,仅仅将栈中的最近一条LR中的obj赋值为null。这里需要注意,MW中的threadId并不会做修改。
3.锁竞争处理流程
持有锁的线程T2并不会在发现竞争的第一时间就直接撤销锁,或者升级锁,而是执行到安全点后再处理。
- 此时如果当前线程已执行完同步块代码且线程已不存活,将会撤销锁,将锁对象恢复至无锁状态,然后进入锁升级逻辑。
- 如果当前线程同步块还未执行完或者线程依然存活,将会走锁升级流程,升级为轻量级锁,且升级完后T2继续持有轻量级锁,继续执行同步代码。
ps:怎么判断是否还在执行同步代码呢?遍历栈中的RL,如果都为null,代表锁已全部释放。
4.批量重偏向和批量撤销
有这样一种场景:如果我们预判竞争不多,大部分情况下是单一线程执行同步块,开启了偏向锁。但是在实际使用环境中,出现了大量的竞争,这时候怎么办呢?停机重新配置参数?恐怕不是最好的方案。如果是我们来设计这个这个Synchronized锁,肯定也会做一些兜底策略。比如这样来做,当某一事件发生了N次,那么就更改一下处理策略?
是的,基本思想差不多,只不过更完善,暂时留一个悬念,在下次揭晓。
【从刷面试题到构建知识体系】Java底层-synchronized锁-2偏向锁篇的更多相关文章
- 【从刷面试题到构建知识体系】Java底层-synchronized锁-1
在技术论坛中,经常看到一种言论:面试造火箭,干活拧螺丝.我们平时写的大部分代码的确是CRDU,再提一个层次,也无非就是揉进去复杂一些的业务逻辑,把一堆的CRDU组合起来. 那么问题来了:我们提倡的研究 ...
- 通过面试题学习零散知识:Java面试题整理
一.如何看待面试题 对于喜欢学习的开发者来说,我们抛开工作和生活的时间,剩余的时间并不多,如果都用于学习的话,也不可能学的下所有感兴趣的技术点,精力也跟不上,我是深感如是.而面试题一般都是零碎的知识 ...
- Java知识体系
Java知识体系 java知识结构.jpg web框架.jpg 计算机课程体系.png 2016-08-19_090929.png 流行的哈希算法生存状况.jpg "JAVA之父" ...
- 最强最全的Java后端知识体系
目录 最全的Java后端知识体系 Java基础 算法和数据结构 Spring相关 数据库相关 方法论 工具清单 文档 @(最强最全的Java后端知识体系) 最全的Java后端知识体系 最全的Java后 ...
- github上最全的资源教程-前端涉及的所有知识体系
前面分享了前端入门资源汇总,今天分享下前端所有的知识体系. 个人站长对个人综合素质要求还是比较高的,要想打造多拉斯自媒体网站,不花点心血是很难成功的,学习前端是必不可少的一个环节, 当然你不一定要成为 ...
- github上最全的资源教程-前端涉及的所有知识体系【转】
github上最全的资源教程-前端涉及的所有知识体系[转自:蓝猫的博客] 综合类 综合类 地址 前端知识体系 http://www.cnblogs.com/sb19871023/p/3894452.h ...
- Atitit 知识图谱解决方案:提供完整知识体系架构的搜索与知识结果overview
Atitit 知识图谱解决方案:提供完整知识体系架构的搜索与知识结果overview 知识图谱的表示和在搜索中的展1 提升Google搜索效果3 1.找到最想要的信息.3 2.提供最全面的摘要.4 ...
- android知识体系
1.Android架构分为4层*应用程序层 Android会同一系列核心应用程序包一起发布,该应用程序包包括email客户端,SMS短消息程序,日历,地图,浏览器,联系人管理程序等.所有的应用程序都是 ...
- unity3d所要知道的基础知识体系大纲,可以对照着学习,不定期更新
本文献给,想踏入3D游戏客户端开发的初学者. 毕业2年,去年开始9月开始转作手机游戏开发,从那时开始到现在一共面的游戏公司12家,其中知名的包括搜狐畅游.掌趣科技.蓝港在线.玩蟹科技.天神互动.乐元素 ...
随机推荐
- 利用Python进行数据分析:【Pandas】(Series+DataFrame)
一.pandas简单介绍 1.pandas是一个强大的Python数据分析的工具包.2.pandas是基于NumPy构建的.3.pandas的主要功能 --具备对其功能的数据结构DataFrame.S ...
- Linux端口占用情况查看
1,查看8010端口是否被占用[root@cloud ~]# netstat -an|grep 8010tcp 0 0 0.0.0.0:8010 0.0.0.0:* LISTEN 2,查看8010是被 ...
- 中缀表达式转后缀表达式(Java代码实现)
后缀表达式求值 后缀表达式又叫逆波兰表达式,其求值过程可以用到栈来辅助存储.例如要求值的后缀表达式为:1 2 3 + 4 * + 5 -,则求值过程如下: 遍历表达式,遇到数字时直接入栈,栈结构如下 ...
- win10下使用Linux命令
下载Cygwin安装包 官网下载地址:https://cygwin.com/install.html 执行下载好的安装程序 选择默认安装路径C:\cygwin64即可,可在C:\cygwin64\bi ...
- Redis开发与运维:linux安装
Linux 安装 我的系统是inux 系统,官网下载 https://redis.io/download redis-5.0.5.tar.gz 解压: 编译安装: 官网和文档说得已经很清楚了,现在就执 ...
- .NET斗鱼直播弹幕客户端(下)
.NET斗鱼直播弹幕客户端(下) 在上篇文章中,我们提到了如何使用.NET连接斗鱼TV直播弹幕的基本操作.然而想要做得好,做得容易扩展,就需要做进一步的代码整理. 本文将涉及以下内容: 介绍如何使用R ...
- win10家庭版升级专业版
在网上随便百度一个产品密钥,记得一定要先断网(这个很重要),否则很难升级. 升级之后发现产品未激活,下载KMS激活一下就可以了.
- Elasticsearch 7.x 之文档、索引和 REST API 【基础入门篇】
前几天写过一篇<Elasticsearch 7.x 最详细安装及配置>,今天继续最新版基础入门内容.这一篇简单总结了 Elasticsearch 7.x 之文档.索引和 REST API. ...
- e课表项目第二次冲刺周期第三天
昨天干了什么? 昨天和我们组的组员商量,确定了第二个界面的框架内容,即内容的输入和完成按钮,然后通过在网上搜索图片资源,然后我们利用ps软件,将图片通通设置了大小,进行了裁剪,最后为我们所利用,实现第 ...
- JavaScript:如何获取某一天所在的星期
我们会遇到的需求的是,获取今天或者某一天所在星期的开始和结束日期. 我们这里来获取今天所在星期的始末日期,我们可以通过(new Date).getDay()来获取今天是星期几,然后再通过这个减去或者加 ...