synchronized加锁机制-偏向锁、轻量级锁、重量级锁

经常会听人说synchronized内部会有偏向锁、轻量级锁、重量级锁,这些对于程序员来说是隐式的,也就是jvm在实际执行过程会根据线程运行情况采用不同的加锁策略,不需要程序员进行干预。java8以后默认使用轻量级锁,发生竞争以后会升级成重量级锁,完全禁用偏向锁了。

以下分析是基于64位的jvm虚拟机进行,32位的jvm虚拟机会稍微有些不同。使用synchronized加锁的过程,与java对象头中的mark字段有关系,对象头在jvm创建对象时都会加上,也就是每个对象都有对象头,当然每一个对象都可以作为synchronized加锁对象。

偏向锁(biased lock)

Java6中引入,默认开启;在Java8的默认关闭;java15标记为deprecated,所以运行java程序时不要主动去打开偏向锁,官方已经不建议使用了。
当加上jvm启动参数:-XX:+UseBiasedLocking
提示如下的信息:Java HotSpot™ 64-Bit Server VM warning: Option UseBiasedLocking was deprecated in version 15.0 and will likely be removed in a future release.
在这里插入图片描述
按照官方说法时,维护成本太高,影响新特性的开发;只有很少的人能够正确理解和使用偏向锁,所以综合考虑,不在继续支持偏向锁。

轻量级锁(thin lock)

java中每个对象都可以作为锁对象,因为由于java的内存布局决定的,java的内存布局主要包括两个部分:对象头和对象属性。
对象头包括两部分:8字节的mark区域和4字节的class指针
下面举个例子:
在这里插入图片描述
当new一个LockObject对象,占用多少字节呢?
8+4+4 = 16 bytes
使用jol输出对象的内存布局如下:
在这里插入图片描述

如果对象计算出来的字节数不是8的倍数,会进行对齐,以达到8的倍数,因为64位,每个指针占8个字节,主要是为了提高寻址效率。但是里面String对象只占用4个字节,是因为java默认开启的指针压缩。

-----------------------------------mark分析----------------------------------------------

mark最后两位用来表示加锁状态:

01表示正常状态,也就是无锁状态
00表示轻量级锁状态
10表示重量级锁状态
11表示垃圾回收状态
刚好使用两位表示4种状态,但是为什么不使用00表示正常状态,这也许是java历史遗留原因吧。

mark前面的62位:

在无锁状态,前面62位会存储hashcode、对象分代年龄等信息。
在加锁状态,前面62位会替换成一个指针,解锁以后会把原来62位的数据还原回来。

在调用加锁的方法时,会在该方法的栈帧中生成一个锁记录对象,然后会把mark前面的62位数据通过cas操作保存到锁记录对象中,同时让mark的前62位保存锁记录对象的地址。解锁的时候就可以把原来62位数据通过cas还原回来。
在这里插入图片描述
在这里插入图片描述

重量级锁(fat lock)

当发生竞争以后,t2.run()和t1.method()发生了竞争,这时候就会自动进行锁升级,轻量级锁这个时候不适用了,会让mark的前62位替换成monitor对象的指针,为什么竞争的时候要使用monitor对象?因为thin lock时使用的是锁记录对象,这个对象不具备让线程等待、阻塞等功能,而monitor是系统提供的专门应对多线程并发的对象,这个monitor内部有owner\entrylist\waitset,owner表示当前持有锁的线程,waitset放的是调用wait方法等待notify唤醒的线程,entrylist放的是竞争锁失败后等待锁释放的线程。

可以看出t1.method()和t2.run()已经发生竞争,这时mark的最后两位变成了10,
而前62位指向了重量级锁monitor对象,输出的锁状态也变成了fat lock.
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值