Synchronize底层实现原理

目录

monitor

对象头

锁的获取与升级


Synchronized是Java用于保存线程安全的一个关键字,对于它的使用在之前已经描述过,这里不再赘述。这里主要描述它底层的工作原理以及实现流程。

一段被Synchronized关键字修饰的代码进行反编译后,会发现两个指令,分别是monitorenter和monitorexit。这两个指令包裹的临界区代码表示同步。

在了解基本流程前需要了解两个关键点,分别是monitor和对象头

monitor

monitor意为监视器,由ObjectMonitor实现,在Java中每个对象中都包含一个monitor,用于线程的同步,因此Java中的每个对象都可以作为锁使用。该对象中包含两个队列_waitSet,_EntryList以及一个对象_owner。

当多线程访问同一段同步代码时,进入_EntryList中,若某线程获取到monitor使用权,则进入临界区执行。此时monitor处于锁定状态,_owner对象指向该线程,锁的计数器+1,表示当前线程持有锁,若未获取到,则处于阻塞状态。若当前线程在执行过程中调用了wait()方法,则释放锁,并进入_waitSet集合。若顺利执行结束,则释放锁,则锁的计数器-1,当计数器为0时,表示该锁为可获取状态。

对象头

Java的对象头中存储了两个对象,分别是klass Pointer(类型指针)和Mark word(标记字段),虾米那分别介绍二者的作用

(1)klass Pointer 是对象指向元数据的指针,JVM虚拟机会通过该指针确定一个对象所属的类

(2)Mark word 用于存储对象自身的运行时数据,例如哈希码,锁的状态标志,线程所持有的锁,因此Mark word是实现轻量级锁和偏向锁的关键。

Mark Word 
状态标志位存储内容
未锁定01哈希码,分代年龄
轻量级锁定00指向锁的指针
重量级锁定10指向重量级锁的指针
可偏向01偏向线程ID

锁的获取与升级

在阐述锁升级的过程前,需要先了解一下四种锁

1.无锁

表示所有线程都可以访问修改锁,但同一时刻,仅有一人成功

2.偏向锁

特点:在但线程执行的情况下,该线程在后续访问时,可以自动获取锁,降低损耗,JVM在对象头中的Mark word字段中设置线程Id表示偏向。

获取流程:

step1:访问Mark word中偏向标志是否为1,若为1,确认可偏向

step2:测试线程Id是否执行当前线程,若是,执行step3

step3:若线程Id为指向,则通过CAS操作竞争锁,竞争成功,修改线程Id,执行临界区资源

step4:失败,说明当前还存在其他线程竞争,即非但线程的情况,升级为轻量级锁

3.轻量级锁

特点:不支持并发,而是多线程串行访问资源,但是每次执行都会重新获取锁,消耗性能

获取流程:

step1:访问临界区代码块时,创建锁记录,即Lock Record对象,用于保存Mark word和对象引用

step2:尝试使用线程的mark word替换锁的mark word

step3:成功,则锁对象中的mark word为Lock Record,执行临界区代码

step4:失败,判断锁对象的mark word是否指向当前线程的栈帧,若是,则说明当前线程拥有锁,直接执行临界区代码,否则有多线程竞争,升级为重量级锁

458df2d22f63ae5b5af51e499168a4d9.png

4.重量级锁

支持并发,同一时间,仅有一个线程可以访问临界区资源,依赖操作系统的互斥锁实现,因此在实现时,需要操作系统做内核态与用户态的接环,资源消耗大。

5.自旋锁

在线程尝试获取锁,但未获取到时,不挂起而是执行一个空循环,以等待锁的释放,再次获取锁,但并不是一直执行空循环,当到达阈值的时候,线程不再等待,该阈值一般为一个上下文的时间。

流程图如下所示:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gurean

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值