锁对象、偏向锁、轻量级锁、重量级锁

4 篇文章 0 订阅

锁对象

在java中任何一个对象都能成为锁对象,java对象在内存中的存储结构主要有以下三个部分:
1、对象头
2、实例数据
3、填充数据
对象头的数据主要是一些运行时的数据,其简单结构如下

长度内存说明
32/64bitmark wordhashcode,GC分代年龄,锁信息
32/64bitclass metadata address指向对象类型数据的指针
32/64bitarray Length数组的长度(当对象为数组时候)

mark word里面存储的数据会随着锁标志位的变化而变化,mark word中可能变化为存储以下5种情况
在这里插入图片描述

从上面可以看出锁的信息是存储在对象头中的mark word里
下面看一段代码

LockObj lockObj = new LockObj();
syncronized(lockObj){
	// ...
}

当我们创建一个对象lockObj时,该对象部分的mark word关键数据如下,

bit fields是否为偏向锁锁标记位
hash001

从图中可以看出偏向锁的标记位位“01”,状态是0,表示该对象还没有被加上偏向锁。(1表示被加上了偏向锁)。该对象被创建出来的那一刻,就有了偏向锁的标志位,这也说明所有对象都是可偏向的,但所有对象的初始状态都为“0”,刚被创建的对象的偏向锁并没有生效

偏向锁

当线程执行到临界区(同步块)时,此时会利用CAS操作,将线程ID,插入到mark word中,同时修改偏向锁的标志位
此时mark word的结构信息如下

bit fileds是否为偏向锁所标志位
threadIdepoch(时间戳)101

此时偏向锁的状态为1,说明偏向锁已经生效‘
偏向锁执行原理
大部分情况下不存在锁竞争,通常是由同一个线程获得,所以引入了偏向锁的概念,在一个线程访问同步块时候,对象头中会记录此线程的threadId,后续此线程进入此同步块时,不需要进行加锁和解锁的操作。

偏向锁基本原理:

1、首先获取对象头中的mark word,判断是否是可偏向状态。
2、如果是可偏向(0)状态,那么就通过cas操作,将当前线程的ThreadId写入mark word中。如果cas成功,表示获取到了偏向锁,接着执行同步块。如果cas失败,说明其他线程竞争到锁,需要撤销已获得偏向锁的线程,并升级为轻量级锁。
3、如果是已偏向状态(1)状态,那么判断一下当前线程的threadId和对象头mark word中的threadId是否一致。如果一致,如果此线程已经成功获取到了锁,不需要再次竞争锁。如果不一致,则说明存在竞争,此时根据另外线程的执行情况,可能做偏向撤销,也可能重新偏向,大部分是升级成了轻量级锁。

锁撤销

偏向锁的撤销 偏向锁的撤销并不是把对象恢复到无锁可偏向状态(因为偏向锁并不存在锁释放的概念),而是在获取偏向锁的过程中,发现cas失败也就是存在线程竞争时,直接把被偏向的锁对象升级到被加了轻量级锁的状态。
对原持有偏向锁的线程进行撤销时,原获得偏向锁的线程有两种情况:

  1. 原获得偏向锁的线程如果已经退出了临界区,也就是同步代码块执行完了,那么这个时候会把对象头设置成无锁状态并且争抢锁的线程可以基于CAS重新偏向但前线程
  2. 如果原获得偏向锁的线程的同步代码块还没执行完,处于临界区之内,这个时候会把原获得偏向锁的线程升级为轻量级锁后继续执行同步代码块
  • 过程:
    1、在一个安全节点(也就是没有线程执行字节码)停止持有锁的线程
    2、遍历线程栈,如果存在锁记录的话,需要修复锁记录和mark word,使其变成无锁状态。
    3、唤醒当前线程,将当前线程升级成轻量级锁。
    所以,如果同步块大部分是由2个以上线程竞争的话,那么偏向锁是一致累赘,我们可以一开始将偏向锁功能关闭。可以通过jvm参数,UsebiasedLocking类设置和关闭偏向锁

轻量级锁:

简单描述下锁撤销后,升级为轻量级锁的过程

1、线程在自己的栈桢中创建锁记录LockRecord.
2、将对象头中的mark word信息复制到刚创建的锁记录LockRecord中
3、将锁记录中的owner指针执行锁对象
4、将锁对象的对象头中的mark word 替换为指向锁记录LockRecord的指针。
此时mark word如下

bit fileds锁标志位
指向LockRecord的指针00

注:00表示轻量级锁
轻量级锁有2种。
1、自旋锁
2、自适应自旋锁

自旋锁

自旋锁就是当另外一个线程竞争锁时候,并不会立即阻塞,而是会原地循环等待,原获得锁的线程释放以后,这个线程就会立马获得锁。
注意,锁在原地等待也会消耗cpu,相当于执行一个啥也没有的for循环。
所以,轻量级所适用于同步块执行很快的场景。可以给线程设置一个循环次数,当线程超过了这个次数,我们就认为,继续使用自旋锁不合适了,此时锁会继续膨胀,升级为重量级锁。默认情况下自旋次数为10次,可以通过-XX:PreBlockSpin来进行更改。

自适应自旋锁

自适应自旋锁是线程循环等待的自旋次数并非是固定的,而是会动态根据实际情况ga改善自旋次数。
比如线程1获得了锁,释放之后,线程2获得了锁,此时线程1又来获取锁,那么虚拟机认为线程1hen很有可能再次获得锁,那么会延长线程1的自旋次数。另外,如果某个线程自旋之后,很少成功获得锁,那么以后这个线程要获取锁的时候,有可能直接忽略掉自旋过程,升级为重量级锁,以免空循环浪费资源。

轻量级锁也是非阻塞同步、乐观锁,因为这个过程并没有把线程挂起,而是让线程空循环等待,串行执行

重量级锁

轻量级锁膨胀后,就升级为重量级锁,重量级锁是依赖对象内部的moniter锁来实现,而moniter有依赖操作系统的MutexLock(互斥锁)来实现的,所以重量级锁也被称为互斥锁
此时mark word的部分数据大体如下:

bit fileds锁标记为
指向Mutex的指针10

为什么说重量级锁开销大?

主要是当系统检测到锁是重量级锁后,会把等待想要获得锁的线程进行阻塞,被阻塞的线程不会消耗CPU,但是阻塞或者唤醒一个线程时,都需要操作系统帮忙,这就需要从用户态切换到内核态,而转换的状态需要消耗很多时间的,有可能比执行用户代码的时间还要长。所以重量级线程开销很大。
互斥锁(重量级锁)也被称为是阻塞同步、悲观锁

总结:

syncronized关键字并非一开始就将该对象加上重量级锁,也是从偏向锁,轻量级锁,再到重量级锁的过程。这个就告诉我们,假如我们一开始就知道某个同步块竞争激烈、很慢的话,那么我们一开始就应该使用重量级锁,从而省掉一些锁转换的开销。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
轻量级、重量级偏向是Java中的三种不同的机制。轻量级是一种乐观,用于在多线程环境下提高并发性能。当竞争不激烈时,轻量级使用CAS(Compare and Swap)操作来获取,避免了线程阻塞,提升了性能。然而,如果竞争激烈,轻量级会膨胀为重量级偏向是在没有竞争的情况下,为了进一步提高性能而引入的机制。偏向允许第一个获得的线程在接下来的执行中,无需再进行同步操作,从而减少了不必要的竞争。当有其他线程试图获取偏向时,偏向会升级为轻量级或重量级。 重量级是一种传统的互斥,它使用操作系统原语(例如互斥量)来实现线程阻塞和唤醒,确保同一时间只有一个线程可以访问被定的资源。重量级适用于竞争激烈的场景,但由于涉及到内核态和用户态之间的切换,会带来较大的性能开销。 总结起来,轻量级偏向都是为了提高并发性能而引入的机制,轻量级适用于竞争不激烈的场景,而偏向适用于没有竞争的情况下。重量级适用于竞争激烈的场景,但会带来较大的性能开销。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [浅谈偏向、轻量级、重量级](https://blog.csdn.net/yaojiqic/article/details/124619021)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Java中的偏向,轻量级, 重量级解析](https://blog.csdn.net/lengxiao1993/article/details/81568130)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Java的升级策略 偏向 轻量级 重量级](https://download.csdn.net/download/weixin_38512659/12749004)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

EmineWang

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

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

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

打赏作者

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

抵扣说明:

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

余额充值