java 锁升级

讲Java锁升级,首先讲一下Java对象的内存布局。
首先Java new 一个对象,生成的对象包括三部分信息。

  1. 对象头(Object Header)
    1. 运行时元数据(Mark Word)
      1. hashcode值
      2. GC年龄
      3. 偏向线程Id
      4. 偏向时间错
      5. 锁状态标志
      6. 线程拥有的锁
    2. 类型指针(Klass Word)
    3. 数据长度(数组对象才有的)
  2. 实例数据
  3. 对齐填充

因为对象的锁信息包含在对象头信息里面,所以在了解锁升级机制前最好了解一下对象的内存布局。

对于对象来说,有这么4种锁状态: 无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态。

(这里以64位JVM为例子,这句话看不懂没关系,不影响你继续看下面)

首先,我们看下四种状态下,对象头(Object Header)中分别是什么样子,因为讲的是有关锁的,所以这里我主要讲一下 运行时元数据(Mark Word)中长什么样子。

在64位的JVM中,对象头整个 Object Header占128bit长度,Mark Word占 64,Klass Word占64。
数组对象的对象头是稍微有一点点不一样:
在64位的JVM中,数组对象的对象头整个 Object Header占 128bit+64bit 长度,加的64bit就是用来存数组长度的。

无锁状态
在这里插入图片描述
biase_lock:偏向锁标志
lock:锁标志
age:GC年龄

偏向锁
在这里插入图片描述
当前线程指针:偏向线程ID
Epoch:偏向时间戳

轻量级锁
在这里插入图片描述
指向线程中Lock Record的指针:这个东西又有点难解释了,要是完全不知道的可以跳过,你就认为是个占了62bit的指针跳过,问题不大。不过,我还是简单的解释下。Lock Record是对轻量级锁的优化,当解释器执行monitorenter字节码轻度锁住一个对象的时候,就会在虚拟机栈的栈帧中分配一个叫Lock Record的空间,这个空间用来存储这个被锁住的对象的MarkWord。(先解释到这了,这个深挖还有很多东西,下次有空再专门解释吧,这段里面很多JVM的相关知识,还是那句话,要是看不懂,就记住这是一个62bit的指针,就过。)

重量级锁
在这里插入图片描述
指向互斥量的指针:这个还是有点难解释,同上,完全不知道的,直接过。还是简单解释下,简单说就是在重量级锁状态下,指向对象监视器monitor的指针。就是说,重量级锁状态下,两个不同的线程如果同时对一个对象竞争,一个线程拿到了,一个线程没拿到,那么这个没竞争到对象的线程就会进入等待状态。这个monitor就是管理等待的线程的,而这个 指向互斥量的指针就是指向这个监视器monitor的。(要是这段没看懂,还是同上,就一个指针,就过。)

小小总结一下一个重点,锁标志:
无锁和偏向锁的锁标志都是:01 ,不同的是biase_lock,无锁是0,偏向锁是1
轻量级锁的锁标志是:00
重量级锁是:10

——————————————————————————————————————
无锁:001
偏向锁:101
轻量锁:00
重量锁:10

有没有人想过,一共四种锁状态,为什么不直接两个bit,来表示呢?这其实是因为还有个状态,就是GC状态,就不再这里解释了。算了,还是说一下吧,嫌啰嗦的可以跳过哦,GC状态就是最后两位为11,算了,说多说了那就再画个图吧。。。(一个挺啰嗦的程序员)
GC状态
在这里插入图片描述
CMS:就是一个垃圾回收器,这个就不解释了。

OK,到这,四种锁状态就简单解释完了。
现在开始说锁升级。。哎,显得有点啰嗦了,希望能耐心读完吧。

锁升级
一个对象刚刚被new出来的时候,就是无锁状态,001(偏向锁标志0,锁标志01)。
当线程来上锁了,首先是由无锁状态变为偏向锁,偏向线程ID会改为这个线程的ID。
如果有线程来竞争了,偏向锁会被撤销,升级到轻量级锁,线程会在自己虚拟机栈的栈帧中生成LockRecord,这里会用到CAS将MarkWord设置为指向自己这个象出的LockRecord的指针,谁成功设置了,谁就得到锁。
如果竞争严重了(线程自旋转了10次或者自旋的线程数目超过CPU核数的一半)jdk1.6之后,升级到重量级锁。
先说到这里了,宿舍十点半关门,不知道大家的学校是不是这种规定,反正我是。。哈哈,先这样了。
如有错误,欢迎指正~

### Java 升级机制原理及过程 在Java中,升级是为了优化同步操作的性能而设计的一种机制。当多个线程尝试访问同一个资源时,JVM会根据不同的情况调整的状态以提高效率。 #### 初始状态:无 当没有任何线程持有对象上的时,该对象处于未定状态。此时的对象头中的Mark Word仅存储对象的元数据信息[^2]。 #### 偏向 一旦有第一个线程请求获取此对象上的,则进入偏向模式。在此阶段,JVM会在对象头部记录下当前拥有的线程ID,并标记为偏向。对于后续来自同一线程对该的操作,无需再执行昂贵的竞争检测逻辑;只要确认是相同的线程再次访问即可直接允许其继续运行。这种处理方式极大地减少了单一线程场景下的同步成本。 ```java synchronized (object) { // Critical section code here... } ``` #### 轻量级 然而,如果有其他不同线程也试图获取相同对象上的,在发现已有另一个活动线程正在占用的情况下,原先持有的偏向会被撤销并转换成轻量级。这时两个或更多线程之间会发生短暂的竞争状况——通过自旋等待的方式轮流争取所有权直到其中一个成功为止。这种方式可以避免立即陷入更耗资源的操作系统级别的阻塞调度流程。 #### 重量级 假如上述自旋未能快速解决问题(即存在长时间持续不断的高并发争用),那么最终将会升级到重量级形式。这意味着所有参与竞争的线程都将被挂起放入操作系统内核态队列里排队等候CPU时间片分配来重新争夺监视器控制权。这显然不是最理想的解决方案因为涉及到上下文切换所带来的巨大开销。 #### 升级顺序总结 整个过程中级别按照如下路径逐步提升: 1. **无** 2. **偏向** —— 当首次遇到新的独占线程时启用 3. **轻量级** —— 发生跨线程共享需求时转变而来 4. **重量级** —— 如果频繁发生冲突则进一步恶化至此形态 值得注意的是,这些变化都是透明地由虚拟机内部管理完成,开发者通常不需要显式干预这一过程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值