多线程学习之sychronized锁升级剖析

多线程学习之sychronized锁升级深入剖析

首先首区分内核态以及用户态

将操作系统分级别
用户态:只能访问用户能够访问的指令
内核态:执行在内核空间的,能访问所有指令

JDK早期,synchronized叫做重量级锁,因为申请锁资源必须通过kernel,系统调用,由JVM用户态到OS内核态,都需要经过用户态到内核态的转换。

认识对象的内存布局

对象的内存布局:当我们new一个对象的时候它里面的内存是如何分布的?
hotspot实现:

class T{
	int m;
}

当我们new如上对象的过程中,放到堆内存里面布局是:
最上面8个字节的markword
4个字节的内存指针(class pointer 通过指针找这个对象是属于哪个类的)
4个字节的instance data 就是T 里的 int 的大小(看有多少个成员变量)
一个对象在hotspot中的实现要求8字节对齐(这个对象的大小务必是8的整数倍)
通过Java Object Layout来对如上描述做一个证明:

public class HelloJOL {
    public static void main(String[] args) throws  Exception{
        Object o = new Object();
        //分析对象并打印结果
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
      /*  synchronized (o){
            System.out.println(ClassLayout.parseInstance(o).toPrintable());
        }*/
    }
}

输出如下结果:

java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes

首先从零开始往后数四个字节 ,又从第四个字节开始往后数四个字节,这里一共是八个字节(是markword),后面从第八个字节往后数四个字节就是内存指针,通过后面的value e5 01 00 f8 就能找到Object.class ,由于此处只有12个字节不能达到8字节的对齐,所以后面在12个字节的位置往后再数四个字节(loss due to the next object alignment)完成对象的对齐。

下面回归正题将上面的注解注释掉,看看sychronized加上这段话是如何输出的:

在这里插入图片描述
发现前两行的markword不同,发现上锁的本质就是在hotspot中改变了markword里面的内容,让markword记录所上锁的内容(markword还记录了hashcode的信息以及GC的信息)
如果要查看markword记录的具体内容可以查看,markOop.hpp里的内容:
在这里插入图片描述
如上注释分为两种一种是32位系统的一种是64位系统的,64位对应如下图所示:
在这里插入图片描述
锁升级的步骤:
在这里插入图片描述
当我们首先new 一个普通对象出来,一旦给这个对象加上sychonized修饰的话,他会升级为偏向锁,如果竞争有点激烈,会升级为轻量级锁(也称自旋锁,无锁态),竞争再加剧则通过操作系统申请重量级锁。
偏向锁和轻量级锁属于用户空间锁,不需要和操作系统交互。
重量级锁是需要向内核申请。
偏向锁概念:哪个线程先来就偏向于哪个,没必要设置竞争机制,只是将当前线程的指针设置在markword里面(无竞争,效率高)
轻量级锁概念:当锁竞争较激烈时,会转为自旋锁,当两个线程同时竞争同一把锁的时候,会在两个线程各自的内部都存储一个LR(Lock Record),这两个线程会通过自旋的方式竞争的将他们各自的Lock Record改变到markword上,一旦一个线程竞争到锁了改变了markword那么另一个线程就会通过CAS自选的方式一直等待当前这个竞争到锁资源的线程释放锁(在用户空间等待)
重量级锁:不在用户空间,这个锁从操作系统中申请,markword里面记录的是Object Monitor,就是JVM空间写的一个C++对象,而这个对象去访问的时候需要经过操作系统。

sychronized是可重入锁,重入次数必须记录,因为要解锁几次必须得对应
如果是偏向锁:
偏向锁(以及轻量级锁)->线程栈->LR+1
过程如下:首先无锁态存储的是identity HashCode,变为偏向锁的时候hashcode存入到LR指向的
displacedmarkword,偏向锁重入一次就再生成一个LR,此时LR的指针指向的是一个null(不需要再记录了,因为我此时已经记录了),解锁的时候就依次弹出最后解锁。
重量级锁:会记录在Object Monitor的一个字段上。

为什么有自旋锁还需要重量级锁?
如果持有锁执行时间较长,如果等待的线程较多,都在自旋等待,自旋是消耗CPU资源的,如果等的时间长,或者自旋
线程多,CPU被大量消耗,重量级锁里面有队列WaitSet,将自旋的线程放进去,将线程冻结到WaitSet中使其不占用CPU资源,等待操作系统调度。
偏向锁是否一定比自选锁效率高?
不一定,在明确知道会有多线程竞争的情况下,偏向锁肯定会涉及锁撤销过程(耗时间),这时候直接使用自旋锁。
JVM启动过程,会有很多线程竞争(明确),所以默认情况启动时不打开偏向锁,过一段时间才打开,默认情况下偏向锁有个时延,默认4s,由于偏向锁启动后没有偏向于任何线程叫做匿名偏向。

自旋锁什么时候升级为重量级锁?
竞争加剧:有线程超过10次自旋 -XX:PreBlockSpin,或者自旋线程数超过CPU核数的一半,1.6之后,加入自适应自选,JVM控制升级重量级锁

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值