问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

synchronized锁升级过程

发布网友 发布时间:2024-10-04 01:40

我来回答

1个回答

热心网友 时间:2024-11-15 19:27

简介

在多线程中解决线程安全的问题时常用到Synchronized,现在的synchronized相对于早期的synchronized做出了优化,从以前的加锁就是重量级锁优化成了有一个锁升级的过程(偏向锁->轻量级锁->重量级锁)。

CAS

cas的全称是compare and swap,从名称上可以看出它是先比较再进行设置,它是一种在多线程环境下实现同步功能的机制。

下面这段代码是在ReentrantLock类中复制的一段关于CAS操作的代码

protected final boolean compareAndSetState(int expect, int update) {// See below for intrinsics setup to support thisreturn unsafe.compareAndSwapInt(this, stateOffset, expect, update);}

compareAndSwapInt的参数,这里的参数一和参数二现在把他理解成是一个参数unsafe.compareAndSwapInt(curr, expect, update);所以这一个Cas操作里面需要三个参数

参数一:当前值

参数二:期望值

参数三:需要修改成的值

只有在当前值和期望值一致的时候才会将当前值修改成参数三所传入的值。

CAS在JUC包中应用很广泛,比如在AtomicXXX类中使用到了大量的CAS操作,

CAS不是很难理解,有个概念就好。

markWord

如果了解对象的内存布局的可以略过此段。这个对象的内存布局是和JVM的实现有关,本章所说的是HotSpot的实现。

当一个对象被创建出来后它在内存中的布局如下,由四部分组成:

8个字节的markword,(markword里面包含了其它的东西,比如GC标记,锁类型)

4个字节的ClassPoint(此指针指向的Class),默认是开启指针压缩所以是四个字节,关闭指针压缩后是八个字节

实例对象中的成员属性大小

字节填充(有的JVM需要8字节对齐,如果上面的字节相加后不能被8整除,则需要在此补齐)

看到上面的图,应该可以大概的看出来synchronized加锁,其实就是修改的对像头里面的markword的数据。所以synchronized可以对任何一个对象加锁

现在有一个Java类T,将它new出来之后它的对象的内存布局是什么样子的呢?

class T{Integer age;}

可以通过一个小工具来查看下这个T类在内存中的对象布局

<dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-core</artifactId><version>0.9</version><scope>compile</scope></dependency>

通过下面的程序来打印下T对象的布局是什么样子的。

public static void main(String[] args) {T o = new T();System.out.println(ClassLayout.parseInstance(o).toPrintable());}

这张图是一个没有加锁的对象的对象布局。

通过synchronized后的对象布局是什么样子的呢?这次再修改下T类,目的是让它存在字节填充

class T{Integer age;Integer age1;}public static void main(String[] args) {T o = new T();synchronized (o){System.out.println(ClassLayout.parseInstance(o).toPrintable());}}

到这里可能有些小伙伴有疑问,这里为啥是轻量级锁,不应该先是偏向锁吗?原因如下:

因为偏向锁是有4秒的延迟的,所以如果想要看到效果可以在代码里加上sleep(4100)就可以了。或者是通过jvm参数-XX:BiasedLockingStartupDelay=0将延迟设置成0

看完这里也对markword有了些了解了,因为在synchronized中加锁就是通过cas的方式修改的markword中的锁状态

Synchronized的锁升级

上图大概就是Synchronized加锁后的一个锁升级的过程。从早期的重量级锁优化到了现在一个轻量级锁。

偏向锁

上面的重量级锁说到重量级锁想要申请一把锁需要用户态到内核态的一个转换,到了后期的JDK版本中,加锁不用在去向OS去申请锁了,只需要在用户态就可以完成加锁。

从名字上可以看出偏向锁它就是偏向某一个线程,把这个锁加到这个线程上,在加锁的时候如果发现当前锁的竞争线程只有一个线程的话,那么这个锁直接偏向这个线程。直接上锁,不存在竞争。并在线程栈中创建一个LR(锁记录)并将markword拷贝到LR中,同时锁中的markwrod中的指针也会指向当前持有锁线程的LR

这里的LR是有什么作用?

首先synchronized是一个可重入锁,它即然是一个可重入锁它就得有一个东西用来记录重入的次数(加锁几次必须解锁几次)。在解锁时LR在栈中弹出一个就表示解锁一次。

当有多个线程竞争的时候会升级成轻量级锁(自旋锁)

通过下图来看下偏向锁是怎么一回事。

当大呆需要上WC时,只有它自已要上WC,此时并没有其它的人需要上WC,那么这时这个WC可以直接给大呆使用,并且大呆把可以标识自已身份的ID贴到门上,表示此时大呆占用了这个WC。

当又有一个线程来抢占锁时发现当前锁已被占用,此时锁会从偏向锁升级成轻量级锁。

匿名偏向

在执行的时候将偏向锁的延迟设置成0-XX:BiasedLockingStartupDelay=0

public static void main(String[] args) throws InterruptedException { T o = new T(); System.out.println(ClassLayout.parseInstance(o).toPrintable()); }

可以看这个程序的执行结果,当前的锁状态是偏向锁,而有意思是的锁存在,但是他并没有指向线程的指针,

这种情况称为匿名偏向。

轻量级锁

说到轻量级锁可能需要在两种情况下来说它,一是在升级成轻量锁之前有偏向锁,另一种是在升级轻量锁之前没有偏向锁,这里说完第一种第二种不用解释各位也会明白是怎么一回事。

还是用上面这个图来解释,此时当前的WC被大呆所占用,这时二呆来了也要使用WC。这时大呆和二呆就要通过CAS的方式来抢占WC。

因为此时锁的状态是偏向锁的状态,二呆来了也要使用WC(这时有两个人同时要使用WC,这时就要将偏向锁升级成轻量级锁),在升级轻量锁之前首先需要将WC上的标识大呆身份的ID撕下来(这一步叫做偏向锁的撤销),然后能过自旋+CAS的方式两个人来抢锁。当其中一个线程抢锁成功后,会将LR贴到WC的门上,表示WC当前被某个线程占用,然后另一个没有抢到锁的线程就一直自旋,当自旋一定次数后升级成重量级锁。

如果在升级轻量锁之前没有偏向锁,此时两个线程直接自旋+CAS的方式来抢锁。

重量级锁

在了解重量级锁之前,我想应该先说下用户态与内核态:

对于系统而言,它可以做的一些事情,普通的应用程序是无法完成的,比如系统可以干掉硬盘,如果普通的程序想要干掉硬盘它必须向操作系统去申请,由此操作系统中的指令分了级别,操作系统级别可以访问所有的指令,在用户态下只能访问用户能访问的指令,如果用户态要访问内核态可以执行的指令必须去向操作系统去申请,请操作系统调用。

在JDK早期,上锁只能上重量级锁。因为,所谓的JVM其实它也是工作在用户态的一个进程,如果想要对一个对象进行上锁,那它必须去向系统去申请锁。申请锁成功后,还需要将这把锁从内核态返回到用户态,它称为重量级锁的原因就是在锁申请的时候都要有一个在用户态到内核态的转换。

当抢占到锁后,markword里面记录的不再是LR的指针,而是指向的是一个C++的对象ObjectMonitor,

如果当前线程自旋一段时间后没有抢到锁就会升级成重量级锁,并将当前的线程存入EntryList队列中阻塞,持有锁的线程执行完成后,在唤醒EntryList队列中的线程去抢占锁。

总结

synchronized的锁升级过程,

偏向锁未启动,创建出来的是普通对象, 如果有一个线程来抢占锁,该锁偏向此线程,这时升级为偏向锁。

当一个线程没有抢占到锁,并且自旋了一定时间后还没有抢到锁,就会升级成重量级锁。

在偏向锁的基础上又来一个线程抢占锁此时升级为轻量级锁

在偏向锁的基础上如果出现了重度竞争就会直接升级成重量级锁

偏向锁已启动,创建出来的对象匿名偏向,后面的锁升级和上面写的一样。

原文:https://juejin.cn/post/7097910013636640776
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
说课包括哪些方面 说课内容包括()。 如何在手机百度上删除对话记录? 结核病是什么样的疾病? 曹丕17岁得了肺痨,明知自己命不长久,还要强争王位,是不是很自私呢?_百... 古代小说常出现的病名 急求一篇"生活小窍门"(500字)的作文 至今最有什么小妙招 健康的戒烟方法 笔记本电池锁死是什么原因引起的? 我在网络平台贷款,也被骗了。合同签了,钱没到账? 格力空调的匹数是什么意思? 现在用python开发后端的公司是django用的多还是flask用的 django和tornado哪个好 关于吉他调弦的问题,求高手指教! 亲们,拜托帮帮我,各位天才高手,我是初一(5)班的现在我有一张班徽的图 ... 出嫁的女儿大年三十能回娘家吗 阻塞值是什么意思? ...最好是比较萌的,可爱的的那种~拜托各位大神帮帮忙~~~ 2024大年三十回娘家过年好吗 2024大年30回娘家有什么说法 ...要唯美浪漫的,玄幻,或酷一点的。拜托各位了,在下感激不尽 怎样理解线程的睡眠,挂起,和阻塞? 谁帮我想一个霸气一点,拽一点的群名,最好带琪字,谢谢,有急用拜托了各位... 大年三十不能在娘家过吗(年三十回娘家对谁不好) 帮我想一个好的群名字,我想把名字起的独有一番韵味?拜托各位大神 vivo手机电量怎么充到100%? 线程阻塞线程阻塞的用法 我是初一五班的学生,我想建一个群,群昵称要起什么好?拜托各位大神 怎么回答家长会上老师的感谢? 穿着新牛仔裤在修车时,不小心沾到了柴油,怎么洗都洗不干净,请问有什么... ...的平台给我发信息说借款到账,到现在没收到一分钱,骗子啊 为什么我的电脑有时候关不了机了,电脑上一直显示正在关机,但就是卡在... 格力如何区分几匹 ...早餐和午餐餐后糖14左右,没有服药,处于饮食控制阶段,是否需要服药... 我今年6月检查餐后两小时血糖21,6月9号检查空腹血糖7,然后我天天跑步控 ... 潮汕结婚多少钱,需要婚前协议吗? 【八股文】java面试常考点:Redis 硬核剖析Redis单线程为什么那么快? 宝宝辅食能过夜吗 现在初三,想在QQ创建群,初三一班,求一个名字 我是一名90末初一的学生我想创一个班级群帮忙想一下群名字要有七年一... 谁能帮我想一个好听的班级群的名字?我们是1班的,并且要方便同学改成统一... 我们建了一个班群,不知道取什么名字好,老师有在里面,可不可以帮我想个... 如何判断肉桂茶的品质优劣? 肉桂茶的味道怎么样? 武夷肉桂的口感有什么特点? 为什么金鱼的尾巴上会有血?? 肉桂茶的口感和香味如何? 我家养的金鱼身上尾巴上怎么会出现 乳白色的斑点,求懂养鱼的大师... 我养的金鱼!鱼尾巴怎么会有白点,是什么原因?怎么给金鱼治病