深入JVM锁机制1

上传人:jin****ng 文档编号:170181138 上传时间:2022-11-19 格式:DOCX 页数:6 大小:124.53KB
收藏 版权申诉 举报 下载
深入JVM锁机制1_第1页
第1页 / 共6页
深入JVM锁机制1_第2页
第2页 / 共6页
深入JVM锁机制1_第3页
第3页 / 共6页
资源描述:

《深入JVM锁机制1》由会员分享,可在线阅读,更多相关《深入JVM锁机制1(6页珍藏版)》请在装配图网上搜索。

1、深入 JVM 锁机制 1-synchronized目前在Java中存在两种锁机制:synchronized和Lock, Lock接口及其实现类 是JDK5增加的内容,其作者是大名鼎鼎的并发专家Doug Lea。本文并不比较 synchronized与Lock孰优孰劣,只是介绍二者的实现原理。数据同步需要依赖锁,那锁的同步又依赖谁? synchronized给出的答案是 在软件层面依赖JVM,而Lock给出的方案是在硬件层面依赖特殊的CPU指令, 大家可能会进一步追问: JVM 底层又是如何实现 synchronized 的?本文所指说的JVM是指Hot spot的6u23版本,下面首先介绍sy

2、nchronized 的实现:synrhronized关键字简洁、清晰、语义明确,因此即使有了 Lock接口, 使用的还是非常广泛。其应用层的语义是可以把任何一个非null对象作为锁, 当synchronized作用在方法上时,锁住的便是对象实例(this);当作用在静 态方法时锁住的便是对象对应的Class实例,因为Class数据存在于永久带,因 此静态方法锁相当于该类的一个全局锁;当 synchronized 作用于某一个对象实 例时,锁住的便是对应的代码块。在HotSpot JVM实现中,锁有个专门的名字: 对象监视器。1. 线程状态及状态转换当多个线程同时请求某个对象监视器时,对象监视

3、器会设置几种状态用来 区分请求的线程: Con ten tion Lis t:所有请求锁的线程将被首先放置到该竞争队列 Ent ry Lis t: Con ten tion List中那些有资格成为候选人的线程被移到 Entry List Wait Set:那些调用wait方法被阻塞的线程被放置到Wait Set OnDeck:任何时刻最多只能有一个线程正在竞争锁,该线程称为OnDeck Owner :获得锁的线程称为Owner !Owner:释放锁的线程 下图反映了个状态转换关系:新请求锁的线程将首先被加入到 ConetentionList 中,当某个拥有锁的线程 (Owner 状态)调用

4、unlock 之后,如果发现 EntryList 为空则从 ContentionList中移动线程到Ent ryLis t,下面说明下Con ten ti onList和Ent ryLis t的实现方 式:1.1 ContentionList 虚拟队列Con tent ionList并不是一个真正的Queue,而只是一个虚拟队列,原因在于Con tent ionList是由Node及其next指针逻辑构成,并不存在一个Queue的数 据结构 ContentionList 是列,每次新加入Node时都会在队头进行,通过CAS改变第一个节点的的指针为新增节点,同时设置新增 节点的 next 指向后

5、续节点,而取得操作则发生在队尾。显然,该结构其实是个 Lock-Free 的队列。因为只有Owner线程才能从队尾取元素,也即线程出列操作无争用,当然也就避 免了 CAS的ABA问题。pollXAdd NOAtomic1.2 EntryListEntryList与ContentionList逻辑上同属等待队列,ContentionList会被线程 并发访问,为了降低对Con ten ti onList队尾的争用,而建立Ent ryLis t。Owner 线程在unlock时会从Con ten tionList中迁移线程到Ent ryLis t,并会指定 EntryList中的某个线程(一般为H

6、ead)为Ready (OnDeck)线程。Owner线程 并不是把锁传递给OnDeck线程,只是把竞争锁的权利交给OnDeck,OnDeck线程 需要重新竞争锁。这样做虽然牺牲了一定的公平性,但极大的提高了整体吞吐量, 在Hotspot中把OnDeck的选择行为称之为“竞争切换。OnDeck 线程获得锁后即变为 owner 线程,无法获得锁则会依然留在 EntryList 中,考虑到公平性,在 EntryList 中的位置不发生变化(依然在队头)。如果 Owner 线程被 wait 方法阻塞,则转移到 WaitSet 队列;如果在某个时刻被 notify/notifyAll 唤醒,则再次转移

7、到 EntryList。2. 自旋锁那些处于ContetionList、EntryList、WaitSet中的线程均处于阻塞状态,阻塞 操作由操作系统完成(在 Linxu 下通过 pthread_mutex_lock 函数)。线程被阻 塞后便进入内核(Linux )调度状态,这个会导致系统在用户态与内核态之间来 回切换,严重影响锁的性能缓解上述问题的办法便是自旋,其原理是:当发生争用时,若Owner线程能在很 短的时间内释放锁,则那些正在争用线程可以稍微等一等(自旋),在Owner 线程释放锁后,争用线程可能会立即得到锁,从而避免了系统阻塞。但Owner 运行的时间可能会超出了临界值,争用线程

8、自旋一段时间后还是无法获得锁,这 时争用线程则会停止自旋进入阻塞状态(后退)。基本思路就是自旋,不成功再 阻塞,尽量降低阻塞的可能性,这对那些执行时间很短的代码块来说有非常重要 的性能提高。自旋锁有个更贴切的名字:自旋-指数后退锁,也即复合锁。很显 然,自旋在多处理器上才有意义。还有个问题是,线程自旋时做些啥?其实啥都不做,可以执行几次for循环,可 以执行几条空的汇编指令,目的是占着 CPU 不放,等待获取锁的机会。所以说, 自旋是把双刃剑,如果旋的时间过长会影响整体性能,时间过短又达不到延迟阻 塞的目的。显然,自旋的周期选择显得非常重要,但这与操作系统、硬件体系、 系统的负载等诸多场景相关

9、,很难选择,如果选择不当,不但性能得不到提高, 可能还会下降,因此大家普遍认为自旋锁不具有扩展性。对自旋锁周期的选择上,Hot Spot认为最佳时间应是一个线程上下文切换的时间, 但目前并没有做到。经过调查,目前只是通过汇编暂停了几个CPU周期,除了自 旋周期选择, HotSpot 还进行许多其他的自旋优化策略,具体如下:如果平均负载小于CPUs则一直自旋如果有超过(CPUs/2)个线程正在自旋,则后来线程直接阻塞如果正在自旋的线程发现Owner发生了变化则延迟自旋时间(自旋计数) 或进入阻塞如果CPU处于节电模式则停止自旋自旋时间的最坏情况是CPU的存储延迟(CPU A存储了一个数据,到CP

10、UB 得知这个数据直接的时间差) 自旋时会适当放弃线程优先级之间的差异那 synchronized 实现何时使用了自旋锁?答案是在线程进入 ContentionList 时,也即第一步操作前。线程在进入等待队列时首先进行自旋尝试获得锁,如果 不成功再进入等待队列。这对那些已经在等待队列中的线程来说,稍微显得不公 平。还有一个不公平的地方是自旋线程可能会抢占了 Ready线程的锁。自旋锁由 每个监视对象维护,每个监视对象一个。3. 偏向锁在 JVM1.6 中引入了偏向锁,偏向锁主要解决无竞争下的锁性能问题,首先我们 看下无竞争下锁存在什么问题: 现在几乎所有的锁都是可重入的,也即已经获得锁的线程

11、可以多次锁住/解锁监 视对象,按照之前的Hot Spot设计,每次加锁/解锁都会涉及到一些CAS操作(比 如对等待队列的 CAS 操作), CAS 操作会延迟本地调用,因此偏向锁的想法是一 旦线程第一次获得了监视对象,之后让监视对象“偏向”这个线程,之后的多次 调用则可以避免CAS操作,说白了就是置个变量,如果发现为true则无需再走 各种加锁/解锁流程。但还有很多概念需要解释、很多引入的问题需要解决:3.1 CAS及SMP架构CAS为什么会引入本地延迟?这要从SMP (对称多处理器)架构说起,下图大概 表明了 SMP 的结构:其意思是所有的CPU会共享一条系统总线(BUS),靠此总线连接主存

12、。每个核 都有自己的一级缓存,各核相对于 BUS 对称分布,因此这种结构称为“对称多处 理器”。而CAS的全称为Compare-And-Swap,是一条CPU的原子指令,其作用是让CPU 比较后原子地更新某个位置的值,经过调查发现,其实现方式是基于硬件平台的 汇编指令,就是说CAS是靠硬件实现的,JVM只是封装了汇编调用,那些 AtomicInteger 类便是使用了这些封装后的接口。Core1 和 Core2 可能会同时把主存中某个位置的值 Load 到自己的 L1 Cache 中, 当 Core1 在自己的 L1 Cache 中修改这个位置的值时,会通过总线,使 Core2 中L1 Cac

13、he对应的值“失效,而Core2 一旦发现自己L1 Cache中的值失效(称 为Cache命中缺失)则会通过总线从内存中加载该地址最新的值,大家通过总线 的来回通信称为“Cache 致性流量”,因为总线被设计为固定的“通信能力”, 如果Cache 一致性流量过大,总线将成为瓶颈。而当Corel和Core2中的值再次 一致时,称为“Cache 一致性”,从这个层面来说,锁设计的终极目标便是减少 Cache 一致性流量。而 CAS 恰好会导致 Cache 一致性流量,如果有很多线程都共享同一个对象,当某 个 Core CAS 成功时必然会引起总线风暴,这就是所谓的本地延迟,本质上偏向 锁就是为了消

14、除CAS,降低Cache 一致性流量。Cache 一致性:上面提到Cache 一致性,其实是有协议支持的,现在通用的协议是MESI (最早 由 Intel 开始支持),具体参考:htt p:/en.wikipedia.org/wiki/MESI_pro to col, 以后会仔细讲解这部分。Cache 一致性流量的例外情况:其实也不是所有的CAS都会导致总线风暴,这跟Cache 一致性协议有关,具体参 考: NUMA(Non Uniform Memory Access Achit ectude 架构:与 SMP 对应还有非对称多处理器架构,现在主要应用在一些高端处理器上,主要 特点是没有总线,没有公用主存,每个Core有自己的内存,针对这种结构此处 不做讨论。3.2 偏向解除偏向锁引入的一个重要问题是,在多争用的场景下,如果另外一个线程争用偏向 对象,拥有者需要释放偏向锁,而释放的过程会带来一些性能开销,但总体说来 偏向锁带来的好处还是大于CAS代价的。4. 总结关于锁,JVM中还引入了一些其他技术比如锁膨胀等,这些与自旋锁、偏向锁相 比影响不是很大,这里就不做介绍。通过上面的介绍可以看出,synchronized的底层实现主要依靠Lock-Free的队 列,基本思路是自旋后阻塞,竞争切换后继续竞争锁,稍微牺牲了公平性,但获 得了高吞吐量。

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!