-
偏向锁原理
2020-09-24 22:30:59偏向锁原理 persisting_关注 0.0932019.01.26 18:58:40字数 2,554阅读 3,238 1 概述 本文介绍偏向锁相关原理,并不限定于Java中的偏向锁,但是Java中偏向锁的实现也是相同的原理,本文主要是对参考文献(Quickly ...偏向锁原理
0.0932019.01.26 18:58:40字数 2,554阅读 3,238
1 概述
本文介绍偏向锁相关原理,并不限定于Java中的偏向锁,但是Java中偏向锁的实现也是相同的原理,本文主要是对参考文献(Quickly Reacquirable Locks)中偏向锁实现重点部分的翻译,加入了自己的理解,参考文献称偏向锁为可快速获取的锁(QRL,Quickly Reacquirable Locks)。如何快速获取将会在第2节介绍过相关数据结构之后介绍。偏向锁主要是为了提高绝大部分情况下不存在竞争、只有一个线程在尝试获取锁的场景,通过相关数据结构可以减少CAS操作的数量,提高应用性能。偏向锁在有多个线程竞争获取时,会变成(论文中称为revoke,撤销)普通的锁(或默认锁),在Java中则会变先变为轻量级锁。
2 相关数据结构
相对于一般的锁来说,偏向锁使用了两个额外的域(这里不能等同于Java类成员域,在Java中其实是使用对象头中的Mark Word保存的)。第一个域是用来记录锁当前状态的域,可能的状态包括NEURAL,BIASED,REVOKING,DEFAULT。
- NEURAL:锁的初始状态,刚建立时的状态。
- BIASED:该锁被第一次获取且没有竞争时,锁状态会被持有的线程置为此状态。
- REVOKING:如果有另一个线程尝试获取当前被其他线程持有的BIASED状态的锁,则该锁会经过撤销过程变为普通的锁,即DEFAULT,撤销过程中该锁会处于中间状态REVOKING。
- DEFAULT:表示因为多个线程竞争获取偏向锁,则该锁会退化为普通的锁。
注意:撤销和释放(release)锁不同,这里的撤销(revoke)指的是从偏向锁退化为普通锁的过程。
初始状态下锁处于NEURAL状态,当该锁被第一次获取时,获取锁的线程会将锁状态变为BIASED。此时如果有另一个线程尝试获取同一个锁,那么其最终会将锁状态变为DEFAULT,在变为DEFAULT状态之前该锁会根据撤销协议先处于一个暂时的中间状态REVOKING。当锁状态为BIASED时,状态中会有一个域用来记录当前持有锁的线程标识符(后文使用线程ID代替)。对于一个不可再偏向(non-rebiasable,表示一旦撤销之后不可再次回到偏向状态)的锁,则锁状态只能按照NEURAL->BIASED->REVOKING->DEFAULT的状态进行切换,不可回退。但是偏向锁也可实现为可重偏向的锁(rebiasable)。
实现偏向锁需要额外增加的第二个域是一个可以指示布尔类型的位(bit)即可,用来表示偏向锁当前持有者对锁是获取状态还是释放状态。有了这个标识,当偏向锁被获取并且没有撤销时,当前持有者线程的获取和释放操作只要简单的改变该域的状态即可。
注意,一个线程获取偏向锁首先需要成为偏向锁的持有者(holder),即将自己的线程ID通过CAS写入锁状态域中,然后再获取锁(acquire),持有并不一定获取了锁,持有之后必须改变上面布尔标识位才获取了锁,但是获取锁之前必须要先成为锁的持有者;释放(release)锁之后也需要有其他线程显示竞争之后当前持有者才会失去持有者身份,锁的持有者可以通过改变上面介绍的布尔位进行锁的获取和释放动作。
将锁从初始的NEUTRAL状态转变为BIASED状态可以仅通过一次CAS操作完成。这里使用CAS操作是必须的,因为需要避免多个线程同时尝试获取NEUTRAL状态锁并置其状态为BIASED。
现在我们介绍第1节概述中快速获取的含义,根据上面的数据结构介绍,当一个线程获取了偏向锁之后,会在锁中记录线程ID,锁中也会有一个标识位用来表示该锁目前是释放的还是被获取的。因此以后线程在获取和释放锁时,只要检测锁是否记录自己的线程ID即可,如果检测成功,表示线程已经是偏向锁的持有者,直接通过改变上述布尔类型标识位域进行获取和释放锁即可。如果测试失败(表示该锁目前持有者不是自己),则再测试一下锁当前状态,如果还是BIASED状态,则使用CAS竞争锁,如果CAS成功,则尝试使用CAS将锁状态中线程ID置为自己的线程ID,这样后续在获取和释放锁时就不用使用CAS操作了。上面测试过程没有使用CAS操作,因此提高了性能,实现了快速获取锁。
实现上面atomic-free(表示尽可能减少CAS这样的原子操作)偏向锁的难点就在于如何协调获取偏向锁和撤销偏向锁的过程。必须处理偏向锁获取和释放同时发生偏向锁撤销时的多线程竞争问题,可以通过使用CAS将锁的状态改为REVOKING来避免后一种竞争:通过CAS将锁状态改为REVOKING也可能会有多线线程同时进行,但是CAS保证只有一个线程会成功改变锁状态,称为真正的撤销者(revoker),其他的线程则尝试着获取默认锁(默认锁即偏向锁撤销完成之后变成DEFAULT状态的锁)。
3 锁操作过程中的场景
下面介绍偏向锁操作过程(获取、释放、撤销等)的四种主要场景:
-
第一种场景是当锁还没有被任何线程获取过处于初始状态NEUTRAL时。在这种状态下,想要获取锁的线程首先会测试锁的状态,然后会使用CAS操作将锁状态中当前持有者线程置为自己的线程ID,这里的CAS操作是必须的,因为需要在多个线程同时获取偏向锁时保证原子性,CAS操作保证了最终只有一个线程会获取到偏向锁,那些获取失败的线程会进入撤销模式,也就是下面会介绍的第3种场景。当一个线程成功的在锁状态中写入自己的线程ID时,即成为该偏向锁的持有者,锁的状态会由NEUTRAL变为BIASED,同时该锁也会被这个线程获取。
-
第二种场景是偏向锁的当前持有者线程重新获取锁。在这种场景下,持有者线程只需要检测所的状态位保存自己的线程ID,再测试该锁状态不为REVOKING,即没有被撤销,最后对布尔状态位进行赋值即可。如果该锁被撤销了,锁的当前持有线程的获取动作会失败,需要进入获取默认锁的路径。
-
第三种场景发生在第一个非偏向锁持有者尝试获取同一个锁时,通过测试锁当前状态为BIASED,并且锁状态中保存的线程ID不是自己的线程ID可以知道进入此场景。该线程会通过CAS操作将锁的状态由BIASED改为REVOKING(REVOKING是偏向锁撤销为普通锁的中间状态,用来表示锁正在撤销过程中)。因为偏向锁的当前持有线程可能会随时改变锁的状态(比如随时获取、释放偏向锁),所以上面的CAS操作会使用循环进行多次尝试,且每次尝试CAS之前都必须重新读取一下锁状态。需要注意的是,可能会存在多个非持有者线程同时使用CAS尝试将锁状态从BIASED改为REVOKING,CAS则保证最终只有一个线程修改成功,修改成功的线程会变成该锁的撤销者(revoker)。其他修改失败的线程会被通知该锁现在已经不是偏向状态了,从而进入自旋状态直到撤销者成功的完成偏向锁的撤销过程,将锁状态改为DEFAULT,这些线程最终会转向去获取普通锁。偏向锁的撤销者会等待偏向锁的当前持有者释放锁,然后撤销者线程会成功的将锁变成普通锁,然后也会成为该普通锁的第一个获取者。
-
第四种场景发生在锁状态为DEFAULT时,进入这个场景有三步:首先,该线程尝试获取初始状态(NEUTRAL)或者偏向锁(BIASED)失败;然后,该线程通过获取普通锁的路径成功获取普通锁;最后,该线程成功获取锁,并从等待锁的阻塞中被唤醒。
参考文献后面还介绍了可再偏向(rebiasable)锁的实现原理,即在合适的时机再次尝试将锁从DEFALUT状态置为BIASED状态,这里不再介绍,有兴趣可以看看参考文献。
参考文献
-
java偏向锁_Java知识进阶-程序员彻底理解偏向锁原理-知识铺
2021-02-12 11:04:38原标题:Java知识进阶-程序员彻底理解偏向锁原理-知识铺知识铺: 致力于打造轻知识点,持续更新每次的知识点较少,阅读不累。不占太多时间,不停的来唤醒你记忆深处的知识点。一、使用场景被synchronized修饰的方法...原标题:Java知识进阶-程序员彻底理解偏向锁原理-知识铺
知识铺: 致力于打造轻知识点,持续更新每次的知识点较少,阅读不累。不占太多时间,不停的来唤醒你记忆深处的知识点。
一、使用场景
被synchronized修饰的方法,如果没有多余的线程来竞争,并且总是由同一个线程多次获取锁。避免每次阻塞线程,从用户态切换到内核态,这种资源的浪费情况引入了偏向锁。
二、偏向过程
对象实例化后,第一个线程来访问,这个时候它是可偏向的,偏向了该线程,让线程持有自己。 线程修改对象头中的 Mark Word 使用CAS操作,并将对象头中的指向线程ID改为指向自己的ID。
同一个线程重入的时候,只需要比较对象中线程ID是否指向自己的ID,如果是自己的,直接持有该对象。
三、偏向流程
3.1 初始化对象
lock = 01 可偏向
ThreadId = 0 无持有线程
3.2 线程1竞争锁
检测 lock == 01 和 ThreadId 是否指向自己 ,如果 “是” 跳转到 3.5 “否” 跳转到3.3
3.3 CAS竞争操作
通过CAS竞争,如果 “成功” 把threadId设置为当前线程1的ID,然后跳转到 3.5。 如果“失败”跳转到 3.4
3.4 CAS竞争失败
说明当前对象,有线程获取到锁。线程进入safepoint(见附录一)位置,并挂起线程,这个时候偏向锁升级为重量锁。继续执行下一步。
3.5 执行同步代码
附录一、
safepoint作用:
标识当前JVM在某个位置是安全的,可以提供暂停。
如:GC的时候,所有线程暂停,调用SafepointSynchronize::begin()方法,最终使所有的线程都进入到safepoint位置,既线程挂起的过程。
safepoint位置:
1. 循环的末尾
这里引入,可以避免某个线程执行时间过长,一直进入不到安全点,导致整个GC被卡住。
防止一直循环先去,线程不进入safepoint,导致其他线程在等待它而不能进入safepoint位置。
2. 方法返回之前
3. 调用方法之后
责任编辑:
-
Java轻量级锁(自旋锁)和偏向锁原理
2013-10-13 10:10:24很多人都会称呼它为重量级锁,但是随着Java SE1.6对Synchronized进行了各种优化之后,有些情况下它并不那么重了,本文详细介绍了Java SE1.6中为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁,... -
Java 多线程 - 锁优化(轻量级锁、偏向锁原理及锁的状态流转)
2020-01-19 12:11:16如适应性自旋、锁消除、锁粗化、轻量级锁和偏向锁等。(主要指的是synchronized的优化)。 适应性自旋 (自旋锁) 为了让线程等待,我们只需要让线程执行一个忙循环(自旋),这项技术就是所谓的自旋锁。引入自旋锁...前言
为了进一步改进高效并发,HotSpot虚拟机开发团队在JDK1.6版本上花费了大量精力实现各种锁优化。如适应性自旋、锁消除、锁粗化、轻量级锁和偏向锁等。(主要指的是synchronized的优化)。
适应性自旋 (自旋锁)
为了让线程等待,我们只需要让线程执行一个忙循环(自旋),这项技术就是所谓的自旋锁。引入自旋锁的原因是互斥同步对性能最大的影响是阻塞的实现,管钱线程和恢复线程的操作都需要转入内核态中完成,给并发带来很大压力。自旋锁让物理机器有一个以上的处理器的时候,能让两个或以上的线程同时并行执行。我们就可以让后面请求锁的那个线程**“稍等一下”**,但不放弃处理器的执行时间,看看持有锁的线程是否很快就会释放锁。为了让线程等待,我们只需让线程执行一个忙循环(自旋),这项技术就是所谓的自旋锁。
自旋锁虽然能避免进入阻塞状态从而减少开销,但是它需要进行忙循环操作占用 CPU 时间,它只适用于共享数据的锁定状态很短的场景。
在 JDK 1.6之前,自旋次数默认是10次,用户可以使用参数
-XX:PreBlockSpin
来更改。JDK1.6引入了自适应的自旋锁。自适应意味着自旋的时间不再固定了,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。(这个应该属于试探性的算法)。
锁消除
锁消除是指虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行清除。锁清除的主要判定依据来源于逃逸分析的数据支持,如果判断在一段代码中,堆上的所有数据都不会逃逸出去从而被其他线程访问到,那就可以把它们当做栈上数据对待,认为它们是线程私有的,同步枷锁自然就无需进行。
简单来说,Java 中使用同步 来保证数据的安全性,但是对于一些明显不会产生竞争的情况下,Jvm会根据现实执行情况对代码进行锁消除以提高执行效率。
举例说明
对于一些看起来没有加锁的代码,其实隐式的加了很多锁,这些也是锁消除优化的对象。例如下面的字符串拼接代码就隐式加了锁:
String 是一个不可变的类,编译器会对 String 的拼接自动优化。在 JDK 1.5 之前,会转化为
StringBuffer
对象的连续append()
操作:每个
append()
方法中都有一个同步块。虚拟机观察变量 sb,很快就会发现它的动态作用域被限制在concatString()
方法内部。也就是说,sb
的所有引用永远不会逃逸到concatString()
方法之外,其他线程无法访问到它,因此可以进行消除。锁粗化
- 如果一系列的连续操作都对同一个对象反复加锁和解锁,频繁的加锁操作就会导致性能损耗。
- 当多个彼此靠近的同步块可以合并到一起,形成一个同步块的时候,就会进行锁粗化。该方法还有一种变体,可以把多个同步方法合并为一个方法。如果所有方法都用一个锁对象,就可以尝试这种方法。
轻量级锁 (@重点知识点)
JDK 1.6 引入了偏向锁和轻量级锁,从而让锁拥有了四个状态:无锁状态(unlocked)、偏向锁状态(biasble)、轻量级锁状态(lightweight locked)和重量级锁状态(inflated)。
重量级排序 :重量级锁 > 轻量级锁 > 偏向锁 > 无锁
先介绍一下HotSpot 虚拟机对象头的内存布局:
上面这些数据被称为Mark Word - 标记关键词。 其中 tag bits 对应了五个状态,这些状态的含义在右侧的 state 表格中给出。除了 marked for gc 状态(gc标记状态),其它四个状态已经在前面介绍过了。
下图左侧是一个线程的虚拟机栈,其中有一部分称为 Lock Record 的区域,这是在轻量级锁运行过程创建的,用于存放锁对象的 Mark Word。而右侧就是一个锁对象,包含了 Mark Word 和其它信息。
简单来讲,轻量锁就是先通过
CAS
操作进行同步,因为绝大部分的锁,在整个同步周期都是不存在线程去竞争的。获取轻量锁过程当中会当前线程的虚拟机栈中创建一个
Lock Record
的内存区域去存储获取锁的记录(类似于操作记录?),然后使用CAS
操作将锁对象的Mark Word更新成指向刚刚创建的Lock Record
的内存区域的指针,如果这个操作成功,就说明线程获取了该对象的锁,把对象的Mark Word 标记成00
,表示该对象处于轻量级锁状态。失败情况就如上所述,会判断是否是该线程之前已经获取到锁对象了,如果是就进入同步块执行。如果不是,那就是有多个线程竞争这个所对象,那轻量锁就不适用于这个情况了,要膨胀成重量级锁。下图是对象处于轻量级锁的状态。
偏向锁 (@重点知识点)
偏向锁的思想是偏向于让第一个获取锁对象的线程,这个线程在之后获取该锁就不再需要进行同步操作,甚至连 CAS 操作也不再需要。
当锁对象第一次被线程获得的时候,进入偏向状态,标记为
|1|01|
(前面内存布局图中说明了,这属于偏向锁状态)。同时使用CAS
操作将线程 ID (ThreadID)
记录到 Mark Word 中,如果CAS
操作成功,这个线程以后每次进入这个锁相关的同步块就不需要再进行任何同步操作。当有另外一个线程去尝试获取这个锁对象时,偏向状态就宣告结束,此时撤销偏向(Revoke Bias)后恢复到未锁定状态或者轻量级锁状态。
引用《阿里手册:码出高效》的描述再理解一次:
- 偏向锁是为了在资源没有被多线程竞争的情况下尽量减少锁带来的性能开销。
- 在锁对象的对象头中有一个
ThreadId
字段,当第一个线程访问锁时,如果该锁没有被其他线程访问过,即**ThreadId
字段为空**,那么JVM让其持有偏向锁,并将ThreadId
字段的值设置为该线程的ID
。当下一次获取锁的时候,会判断ThreadId
是否相等,如果一致就不会重复获取锁,从而提高了运行效率。 - 如果存在锁的竞争情况,偏向锁就会被撤销并升级为轻量级锁。
可以结合下面这张锁的状态流转图理解一下:
上图实际上是摘自《深入理解Java虚拟机》,自己重新画了一次。在画图的过程当中,发现图中有两个点不是很理解,书中也没有对应的解释。就是偏向锁的重偏向和撤销偏向时如果判断对象是否已经锁定?
后面经过一段时间的查询才知道,HotSpot支持存储释放偏向锁,以及偏向锁的批量重偏向和撤销。这个特性可以通过JVM的参数进行切换,而且这是默认支持的。
Unlock状态下Mark Word的一个比特位用于标识该对象偏向锁是否被使用或者是否被禁止。如果该bit位为0,则该对象未被锁定,并且禁止偏向;如果该bit位为1,则意味着该对象处于以下三种状态:
- 匿名偏向(Anonymously biased)
在此状态下thread pointer
为NULL(0)
,意味着还没有线程偏向于这个锁对象。第一个试图获取该锁的线程将会面临这个情况,使用原子CAS
指令可将该锁对象绑定于当前线程。这是允许偏向锁的类对象的初始状态。 - 可重偏向(Rebiasable)
在此状态下,偏向锁的epoch
字段是无效的(与锁对象对应的class的mark_prototype的epoch值不匹配)。下一个试图获取锁对象的线程将会面临这个情况,使用原子CAS指令可将该锁对象绑定于当前线程**。在批量重偏向的操作中,未被持有的锁对象都被至于这个状态,以便允许被快速重偏向**。 - 已偏向(Biased)
这种状态下,thread pointer
非空,且epoch
为有效值——意味着其他线程正在持有这个锁对象。
这部分因为我目前暂时不想钻研这么深,就简单描述了一下状态流转机制,就当给自己留个坑先记录一下。想要更深的理解知识的话请需要参考下面的文章(使用关键词"bias revocation"进行搜索观看,第二篇写的很好,之后肯定要全篇好好拜读):
-
Eliminating Synchronization-Related Atomic Operations with Biased Locking and Bulk Rebiasing (这个是pdf版ppt文件,需要翻墙哦)
-
Evaluating and improving biased locking in the HotSpot Virtual Machine(这篇我还查到了中文翻译,只不过只翻译了一点,也不保证翻译质量,看原文实际上更好点,讲的很透彻,详细讲了JVM偏向锁的机制,原理,批量重偏向、撤销偏向的操作,相关章节就在下方截图)
StackOverflow上关于这个议题还有一个很有意思的问题,有兴趣的可以去看看。Does Java ever rebias an individual lock
通俗点总结
-
偏向锁是适用于很长一段时间(抽象意义上)都是只有一个线程进入临界区的情况,使用
ThreadId
进行标记; -
轻量锁是适用于会较轻的锁竞争情况,多个线程交替进入临界区,使用
CAS
获取锁对象; -
重量级锁(互斥同步)适用于较严重的锁竞争情况,多个线程同时进入临界区,这个情况下多个线程如果是等待轻量级锁的话,就需要多个线程一直自旋,CPU时间会损失很多;
参考
-
Java多线程优化之偏向锁原理分析
2015-05-12 18:14:34本文来自Ken Wu's Blog,原文标题:《Java偏向锁实现原理(Biased Locking)》 阅读本文的读者,需要对Java轻量级锁有一定的了解,知道lock record, mark word之类的名词。 Java偏向锁(Biased Locking)是Java ...本文来自Ken Wu's Blog,原文标题:《Java偏向锁实现原理(Biased Locking)》
阅读本文的读者,需要对Java轻量级锁有一定的了解,知道lock record, mark word之类的名词。
Java偏向锁(Biased Locking)是Java 6引入的一项多线程优化。它通过消除资源无竞争情况下的同步原语,进一步提高了程序的运行性能。
轻量级锁也是一种多线程优化,它与偏向锁的区别在于,轻量级锁是通过CAS来避免进入开销较大的互斥操作,而偏向锁是在无竞争场景下完全消除同步,连CAS也不执行(CAS本身仍旧是一种操作系统同步原语,始终要在JVM与OS之间来回,有一定的开销)。
所谓的无竞争场景,举个例子,就是单线程访问带同步的资源或方法。
偏向锁实现原理
偏向锁,顾名思义,它会偏向于第一个访问锁的线程,如果在接下来的运行过程中,该锁没有被其他的线程访问,则持有偏向锁的线程将永远不需要触发同步。
如果在运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线程会被挂起,JVM会尝试消除它身上的偏向锁,将锁恢复到标准的轻量级锁。(偏向锁只能在单线程下起作用)通过下图可以更直观的理解偏向锁:
这张图,省略了轻量级锁相关的几处步骤,将关注点更多地聚焦在偏向锁的状态变化上。
偏向模式和非偏向模式,在下面的mark word表中,主要体现在thread ID字段是否为空。挂起持有偏向锁的线程,这步操作类似GC的pause,但不同之处是,它只挂起持有偏向锁的线程(非当前线程)。
这里提到的最近的一个lock record,其实就是当前锁所在的stack frame上分配的lock record。
整个步骤是从偏向锁恢复到轻量级锁的过程。偏向锁也会带来额外开销
在JDK6中,偏向锁是默认启用的。它提高了单线程访问同步资源的性能。
但试想一下,如果你的同步资源或代码一直都是多线程访问的,那么消除偏向锁这一步骤对你来说就是多余的。事实上,消除偏向锁的开销还是蛮大的。所以在你非常熟悉自己的代码前提下,大可禁用偏向锁 -XX:-UseBiasedLocking 。
-
java线程安全与自旋锁,轻量级锁,偏向锁原理解析
2019-07-19 15:38:09java线程安全,锁优化 互斥同步 互斥是实现同步的手段,临界区、互斥量、信号量都是主要的互斥实现方式。Java中最基本的互斥手段就是synchronized关键字,synchronized关键字在编译后,会在同步块前后分别形成... -
Java多线程优化之偏向锁原理分析(转载)
2012-11-21 16:28:53本文详细介绍了Java偏向锁的实现原理。偏向锁是Java 6引入的一项多线程优化。Java多线程优化的实现一般有轻量级锁和偏向锁两种方法。 AD: 本文来自Ken Wu's Blog,原文标题:《Java偏向锁实现... -
偏向锁的基本原理
2021-01-27 14:14:54偏向锁的基本原理 前面说过,大部分情况下,锁不仅仅不存在多线程竞争,而是总是由同一个线程多次获得,为了让线程获取锁的代价更低就引入了偏向锁的概念。怎么理解偏向锁呢?当一个线程访问加了同步锁的代码块时,... -
java 偏向锁的作用_Java偏向锁实现原理(Biased Locking)
2021-02-13 00:42:30http://kenwublog.com/theory-of-java-biased-locking阅读本文...可以参考我的一篇博文:Java轻量级锁原理详解(Lightweight Locking)Java偏向锁(Biased Locking)是Java6引入的一项多线程优化。它通过消除资源无竞争... -
锁原理:偏向锁、轻量锁、重量锁
2017-12-18 16:15:16java中每个对象都可作为锁,锁有四种级别,按照量级从轻到重分为:无锁、偏向锁、轻量级锁、重量级锁。每个对象一开始都是无锁的,随着线程间争夺锁,越激烈,锁的级别越高,并且锁只能升级不能降级。 -
java 偏向锁_java 偏向锁、轻量级锁及重量级锁synchronized原理
2021-02-12 10:24:35Java对象头与Monitorjava对象头是实现synchronized的锁对象的基础,synchronized使用的锁对象是存储在Java对象头里的。对象头包含两部分:Mark Word 和 Class Metadata Address其中Mark Word在默认情况下存储着对象... -
偏向锁状态转移原理
2018-12-18 00:04:00为什么需要偏向锁 当多个处理器同时处理的时候,通常需要处理互斥的问题。 一般的解决方式都会包含acquire和release这个两种操作,操作保证,一个线程在acquire执行之后,在它执行release之前,其它线程不能完成... -
Java偏向锁实现原理(Biased Locking)
2017-04-23 17:37:09可以参考我的一篇博文:Java轻量级锁原理详解(Lightweight Locking) Java偏向锁(Biased Locking)是Java6引入的一项多线程优化。它通过消除资源无竞争情况下的同步原语,进一步提高了程序的运行性能。 轻量级... -
偏向锁、轻量级锁、重量级锁、自旋锁原理讲解
2018-02-09 13:34:46一、简介 在讲解这些锁概念之前,我们要明确的是这些锁不等同于Java API中的ReentratLock这种锁,这些锁是概念上的,是JDK1.6中为了对synchronized同步关键字进行优化而产生的的锁机制。这些锁的启动和关闭策略可以... -
【并发编程系列2】synchronized锁升级原理分析(偏向锁-轻量级锁-重量级锁)
2020-05-23 21:40:00synchronized原理分析初识 synchronizedsynchronized 用法锁是如何存储的synchronized 锁升级偏向锁偏向锁的获取偏向锁的撤销偏向锁及撤销流程图偏向锁注意事项轻量级锁轻量级锁加锁轻量级锁解锁轻量级锁及膨胀流程... -
详细讨论Java中偏向锁、轻量级锁及重量级锁实现原理
2019-01-16 17:32:17最近因为工作关系遇到了很多Java并发编程的问题,然后恶补了一下,现在就来说说Java目前的锁实现原理 其实在JDK1.5以前的早期版本,还没有...偏向锁和轻量级锁都属于乐观锁,偏向锁指的是没有其他线程竞争资源,只... -
Java偏向锁实现原理(BiasedLocking)
2010-04-21 14:44:00轻量级锁也是一种多线程优化,它与偏向锁的区别在于,轻量级锁是通过CAS来避免进入开销较大的互斥操作,而偏向锁是在...偏向锁实现原理 偏向锁,顾名思义,它会偏向于第一个访问锁的线程,如果在接下来的运行过程中... -
synchronized原理和偏向锁、轻量级锁、重量级锁的升级过程
2020-07-29 15:33:30synchronized原理,偏向锁,轻量级锁,重量级锁,锁升级 -
Java锁,偏向锁,轻量级锁,重量级锁原理(转载)
2020-05-08 17:07:32java中的锁 自旋锁 自旋锁原理非常简单,如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋),等持有锁的线程...