第2章 Java并发机制的底层实现原理
2.2 synchronized的实现原理与应用
当一个线程A执行字节码时遇到monitorenter指令时,会首先检查该指令关联的Object的对象头中的Mark Word状态。
2.2.1 如果是偏向锁
如果2bit标志位为01代表此时处于偏向锁状态。
如果2bit标志位为01且1bit的标志位为1,代表该对象此时处于偏向锁且锁被获取状态。
- 如果Mark Word的线程ID是当前线程的ID,代表该线程之前已经获得了该偏向锁,那么可以继续执行同步代码块
- 如果当前Mark Word存的线程的ID不是当前线程ID,结合之前的条件可以推测出有别的线程获得了偏向锁。那么根据Mark Word里存的获得了偏向锁线程的ID记为B线程,如果B线程此时不处于活动状态,那么重新设置Mark Word,把此时的Mark Word设置为无线程获得偏向锁的偏向锁状态,然后A线程去执行CAS操作修改Mark Word的ThreadID,让该对象的偏向锁指向A线程;如果B线程还处于活动状态,那么去检查B线程是否需要继续持有偏向锁,即B线程是否还执行临界区,如果不是,也要“重新设置Mark Word,把此时的Mark Word设置为无线程获得偏向锁的偏向锁状态,然后A线程去执行CAS操作修改Mark Word的ThreadID,让该对象的偏向锁指向A线程”,如果很不幸的B线程处在活跃的状态并且也要继续执行临界区,此时的情况是AB两个线程同时需要进入临界区,偏向锁已经无能为力,偏向锁升级我轻量锁
如果2bit标志位为01且1bit的标志位为0,代表该对象处于偏向锁未被获取的状态,
那么执行CAS操作修改ThreadID。如果此时失败了呢?书上没说,我猜测是如果CAS操作失败那么一定是还有另一个线程此时也在CAS操作修改ThreadID。此时应该也要膨胀到轻量锁。
2.2.2 如果是轻量级锁
这里不同的资料给出了不同的解释,即关于什么时候一个线程会请求一个轻量级的锁。https://www.jianshu.com/p/c5058b6fe8e5我比较认同这里的看法,当mark word的标志位是001时,即当前无锁状态且不允许使用偏向锁的时候,就会请求使用轻量级锁。这一点和那副著名的图有所区别。
当试图获得一个轻量级锁的时候,1、首先在当前线程的栈帧中分配一个Lock Record记录,并把MarkWord复制到当前的LockRecord中 2、尝试使用CAS操作去把对象头的mark word改成指向当前LockRecord的指针
inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value) {
return (intptr_t)cmpxchg((jint)exchange_value, (volatile jint*)dest, (jint)compare_value);
}
上面这段c++就是获取偏向锁的过程,重点关注红色框里的CAS操作。下面就CAS方法的签名,可以看到第一个参数是要改变的值,最后一个参数是用来比较的值。所以综上可以看到获取轻量级锁的本质就是拿自己LockRecord里的markeord和此时此刻对象头的markWord比较并执行cas操作,成功了,当前线程就获得了轻量级锁。
那如果失败了呢?我虽然不太能看懂这段c++,但是我能看懂if逻辑。如果失败了的话,会把当前线程栈帧的lockRecord里的markword替换成一个名为unused_mark的东西(这东西是啥?),然后剩下的就是进入了inflate膨胀成重量级锁的阶段了。这和书上不一样啊,不是自旋吗?????
书还能错了???
上面是轻量级锁的释放逻辑,取出LockRecord里的markword记录,用cas操作把对象头里markword换回去,成功了就释放了轻量级锁,如果失败了就inflate成重量级锁。
仔细看一下这个cas操作,第一个参数是dhw,dhw就是lockrecord里存储的加锁的时候锁对象的mark,也就是说dhw是我想写回的数据。cas里第三个参数是mark,往上看发现mark是object->mark取出来的,也就是我先从锁对象里取出了mark的值,然后把取出的mark的值作为cas操作compare的值。,所以理论上这个cas操作一定是会成功的,如果失败了,说明在从取出mark到使用mark执行cas操作之间对象的mark被别人替换了。什么情况下会被替换呢?再取出mark的时候,mark已经在轻量级锁获取的时候被替换成了指向lockrecord的指针,所以markWord不会因为别的线程尝试获取轻量级锁被替换成指向别的线程lockrecord的指针。所以我猜测,maekword被替换是由于别的线程视图获取轻量级锁,发生了锁竞争,进一步锁膨胀成重量级索,在膨胀的时候markword被更改,不是指向lockrecord的指针。
结合轻量级锁的获取和竞争可以看到,轻量级锁的膨胀有可能是两个地方发生,假设A线程获得了轻量级锁,然后B线程也想获得锁,那么在B线程试图获取轻量级锁的时候,会走到inflate这段逻辑里,所以此时锁膨胀成了重量级锁。然后在A线程释放锁的时候,发现释放锁的CAS操作失败,因为markword不是指向自己lockrecord的指针了,此时A线程也会走inflate的锁膨胀逻辑。
2.2.2 最不想看到的重量级锁
第2章 Java并发机制的底层实现原理的更多相关文章
- 【java并发编程艺术学习】(三)第二章 java并发机制的底层实现原理 学习记录(一) volatile
章节介绍 这一章节主要学习java并发机制的底层实现原理.主要学习volatile.synchronized和原子操作的实现原理.Java中的大部分容器和框架都依赖于此. Java代码 ==经过编译= ...
- (第二章)Java并发机制的底层实现原理
一.概述 Java代码在编译后会变成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码,最终需要转化为汇编指令在CPU上执行,Java中所使用的并发机制依赖于JVM的实现和CPU的指令. ...
- 【java并发编程艺术学习】(五)第二章 java并发机制的底层实现原理 学习记录(三) 原子操作的实现原理学习
章节介绍 主要包括 术语定义.处理器如何实现原子操作.Java如何实现原子操作: 原子(atomic)本意是 不能再进一步分割的最小粒子,“原子操作” 意为 不可被中断的一个或一系列操作. 术语定义 ...
- 【java并发编程艺术学习】(四)第二章 java并发机制的底层实现原理 学习记录(二) synchronized
章节介绍 本章节主要学习 Java SE 1.6 中为了减少获得锁 和 释放锁 时带来的性能消耗 而引入的偏向锁 和 轻量级锁,以及锁的存储结构 和 升级过程. synchronized实现同步的基础 ...
- Java并发机制的底层实现原理之volatile应用,初学者误看!
volatile的介绍: Java代码在编译后会变成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码,最终需要转化为汇编指令在CPU上执行,Java中所使用的并发机制依赖于JVM的实现 ...
- 《Java并发编程的艺术》Java并发机制的底层实现原理(二)
Java并发机制的底层实现原理 1.volatile volatile相当于轻量级的synchronized,在并发编程中保证数据的可见性,使用 valotile 修饰的变量,其内存模型会增加一个 L ...
- Java 并发系列之二:java 并发机制的底层实现原理
1. 处理器实现原子操作 2. volatile /** 补充: 主要作用:内存可见性,是变量在多个线程中可见,修饰变量,解决一写多读的问题. 轻量级的synchronized,不会造成阻塞.性能比s ...
- 《Java并发编程的艺术》读书笔记:二、Java并发机制的底层实现原理
二.Java并发机制底层实现原理 这里是我的<Java并发编程的艺术>读书笔记的第二篇,对前文有兴趣的朋友可以去这里看第一篇:一.并发编程的目的与挑战 有兴趣讨论的朋友可以给我留言! 1. ...
- Java并发机制和底层实现原理
Java代码在编译后会变成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码转化为汇编指令在CPU上执行.Java中的并发机制依赖于JVM的实现和CPU的指令. Java语言规范第三版中 ...
随机推荐
- java使用POI将数据导出放入Excel
本文主要是将数据库取出的数据按照自定义的行列格式导出到excel中,POI则是实现我们需求所用到的技术. POI介绍 使用spring boot导入相关依赖 获取数据(自行处理) 完整代码实例:创建e ...
- Java基础-一文搞懂位运算
在日常的Java开发中,位运算使用的不多,使用的更多的是算数运算(+.-.*./.%).关系运算(<.>.<=.>=.==.!=)和逻辑运算(&&.||.!), ...
- 10个最佳Node.js企业应用案例:从Uber到LinkedIn
译者按: Node.js 8已经发布了,NPM模块每周下载量早已超过10亿,从Uber到LinkedIn都在使用Node.js,谁说JavaScript不能写后台? - 原文: 10 best Nod ...
- html/css的学习之路(2)
今天我跟大家说一下html,html里面其实什么东西都可以放进去的.首先创建一个文本,改变他的后缀名为html.这时有个最重要的一点不管是html还是css都不可以用中文和符号命名. 这是html中的 ...
- jQuery 对AMD的支持(Require.js中如何使用jQuery)
AMD 模块 AMD(异步模块定义,Asynchronous Module Definition)格式总体的目标是为现在的开发者提供一个可用的模块化 JavaScript 的解决方案. AMD 模块格 ...
- Python 一键上传下载&一键提交文件到SVN入基线工具
一键上传下载&一键提交文件到SVN入基线工具 by:授客 QQ:1033553122 实现功能 1 测试环境 1 使用说明 1 注: 根据我司项目规则订制的一套工具,集成以下功能,源码 ...
- .NET Core 2.0
下载 Visual Studio 2017 version 15.3 下载 .NET Core 2.0 下载 Visual Studio for Mac 微软今天发布了.NET Core 2.0 版本 ...
- JSON语法与JavaScript语法的区别
JSON是独立于语言存在的,在不同的编程语言中对这种数据类型的实现不同,例如在JavaScript中使用JavaScript对象对这种数据格式进行实现,那么在java中当然是用java对象实现. 描述 ...
- VMware安装Centos7后有线线缆被拔出
背景:在win10 系统中的虚机软件VMware Workstation中安装CentOS7桌面版,安装过程中没有设置网络 1.确认你win10系统打开了这两个服务:VMware DHCP Servi ...
- 自动化测试基础篇--Selenium获取元素属性
摘自https://www.cnblogs.com/sanzangTst/p/8375938.html 通常在做断言之前,都要先获取界面上元素的属性,然后与期望结果对比. 一.获取页面title 二. ...