简单介绍 java 对象头

本文详细介绍了Java对象头的锁状态,包括无锁、偏向锁、轻量级锁和重量级锁的升级过程。通过JOL工具观察对象头信息,解释了锁升级的各种场景,并提供了代码示例进行验证。强调了-XX:BiasedLockingStartupDelay参数对偏向锁的影响,以及锁升级后无法降级的事实。同时提到了调用wait方法、hashCode方法对锁状态的影响。
摘要由CSDN通过智能技术生成

java 锁升级

顺序:无锁 - 偏向锁 - 轻量级锁 - 重量级锁

我们可以通过使用一个 jar 包 jol 观察对象头,查看第一个字节后三位锁的信息。jol 的 maven 坐标:

		<dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.8</version>
        </dependency>

记住这一个 api 就行了,我只用到了一个:

System.out.println(org.openjdk.jol.info.ClassLayout.parseInstance(new Object()).toPrintable());

必须加个启动参数 -XX:BiasedLockingStartupDelay=0,不然看不到效果,jdk 1.6 之后默认就是启动偏向锁的,所以不用显式开启偏向锁。但是偏向锁有个 delay,看参数也知道,这个 delay 一般得有五秒,我们是不可能等的,所以把 delay 设置成 0.

直接说结论:

  1. 一上来是无锁的 第一个字节后三位(后面简称后三位)是 101,
  2. 如果有一个线程拿到了这个对象的监视器锁,那后三位还是 101,不过后面几个字节会存储和这个线程相关的信息
  3. 如果又来一个线程,这时有两种情况:前一个线程死了,那虽然新线程获取到锁,还是偏向锁,后三位还是 101,后面线程信息也是不变的;如果前面的那个线程没死,这样就发生了竞争,会导致锁升级,此时又有两种情况:如果第一个线程执行完了但没死,则升级成轻量级锁,后三位 000;如果第一个线程没执行完,发生了实质性的竞争,它会先自旋个十几二十次,还没拿到锁的话直接成为重量级锁,后三位 010。如果这时候又来了一个线程,也就是有三个线程在抢监视器锁,那直接升级为重量级锁。
  4. 如果在同步块里调用了 wait 方法,那直接升级成重量级锁
  5. 如果调用过同步对象的 hashCode 方法,那就没法偏向了,因为后面几位保存了对象 hashCode 值,没办法再保存线程栈帧信息了。此时一上来就是轻量级锁。

锁升级后没法降级,也就是升上去就回不来了,只能在有锁和无锁之间切换。比如升到轻量级锁,那没办法再偏向了。

简单贴一下代码,省的有人说我写的没头没尾

		Object lock = new Object();
        System.out.println("no lock");
        System.out.println(ClassLayout.parseInstance(lock).toPrintable());

        synchronized (lock) {
            System.out.println("biased lock");
            System.out.println(ClassLayout.parseInstance(lock).toPrintable());
        }

        System.out.println("no lock");
        System.out.println(ClassLayout.parseInstance(lock).toPrintable());

        // 这时候会变成轻量级锁,因为前一个线程 main 没有死
        // 如果前面那个同步块在非main线程执行,而且执行到这时那个线程死了,那这里还是偏向锁,不会成为轻量级锁
        Thread t = new Thread(() -> {
            synchronized (lock) {
                System.out.println("t1: light weight lock");
                System.out.println(ClassLayout.parseInstance(lock).toPrintable());
            }
        });
        t.start();
        t.join();

        // 前一个线程已经死了,现在不会成重量级锁
        t = new Thread(() -> {
            synchronized (lock) {
                System.out.println("t2: light weight lock");
                System.out.println(ClassLayout.parseInstance(lock).toPrintable());
            }
        });
        t.start();
        // 发生了实际竞争,应该成重量级锁了
        t = new Thread(() -> {
            synchronized (lock) {
                System.out.println("t3: heavy weight lock");
                System.out.println(ClassLayout.parseInstance(lock).toPrintable());
            }
        });
        t.start();

你们去试吧,绝对和我说的一样,我已经测过了。别忘了加 jvm 参数 -XX:BiasedLockingStartupDelay=0,很关键

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值