锁的升级过程(代码演示)

1 前言

        锁的状态有4种,无锁偏向锁轻量级锁重量级锁。那为什么会有锁升级的这种概念,其实大家都知道synchronized 在1.6之后做了升级,但具体是升级了什么?其实在jdk1.6之前锁只有重量级锁这个概念(但是重量级锁需要向内核申请额外的锁资源,涉及到用户态和内核态的切换,比较浪费资源)。升级了之后就不需要直接从无锁到重量级锁了,所以做了锁升级。

        偏向锁:当只有一个线程争抢锁资源的时候,将线程拥有者标示为当前线程。

        轻量级锁(自旋锁):一个或多个线程通过CAS去争抢锁,如果抢不到就一直自旋,知道超过自旋次数,升级为重量级锁。

        重量级锁:多个线程争抢锁,向内核申请锁资源,将为抢到的锁放在队列中直接阻塞。

锁升级过程:

1 当只有一个线程a去竞争资源时,会先使用偏向锁,就是给一个标识,说明这个锁正在被线程a占有。

2 当线程a获取锁之后,线程b、线程c又来竞争锁,发现锁被线程a获取,于是撤销偏向锁的标识,线程a、b、c开始通过CAS去竞争锁

3 当其中一个线程获取锁之后(假如是a),然后线程b和线程c一直自旋获取锁,等到自旋到一定次数依然没有获取锁时,就将锁升级为重量级锁,向内核升级资源,直接将等待的线程阻塞。

2 对象在内存中的布局

对象头在主要包括两部分数据:Mark Word(标记字段)和 Klass Pointer(类型指针)

Mark Word:默认存储对象的HashCode,分代年龄和锁标志位信息。这些信息都是与对象自身定义无关的数据,所以Mark Word被设计成一个非固定的数据结构以便在极小的空间内存存储尽量多的数据。它会根据对象的状态复用自己的存储空间,也就是说在运行期间Mark Word里存储的数据会随着锁标志位的变化而变化。

Klass Point:对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

其实synchronized 用的锁是存在对象头里的,具体是存在对象头的Mark Word中。那Mark Word在对象头中存储了什么呢?

64位的虚拟机:

在这里插入图片描述

32位的虚拟机:

在这里插入图片描述

 可以发现锁标志位分别是:无锁(01)偏向锁(01)轻量级锁(00),重量级锁(10)

3 代码演示锁升级过程

引入依赖:

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

测试代码:

public class TestB {

    private byte test = 1;

    public static void main(String[] args) {
        TestB testB = new TestB();
        System.out.println(ClassLayout.parseInstance(testB).toPrintable());
    }

}

测试结果:

序号1:请求头mark Word占8个字节

序号2:请求头Klass Pointer占4个字节

序号3:成员变量信息,一个byte类型变量占一个字节,多出来的3个字节间隙填充补齐(alignment/padding gap)

序号4:成员变量信息,一个string类型占4个字节

序号5:补充4个字节变成8的倍数。

锁升级过程代码:

public class TestB {

    static Object object = new Object();
    public static class LockThread implements Runnable{

        @Override
        public void run() {
            synchronized (object){
                //进行一些耗时操作
                String test="";
                for (int i = 0; i < 1000; i++) {
                    test += "2";
                }
            }
        }
    }

    public static void main(String[] args) {
        //只有一个线程去抢占锁
        new Thread(new LockThread()).start();
        System.out.println("===========偏向锁===========");
        System.out.println(ClassLayout.parseInstance(object).toPrintable());

        //再加一个线程,两个线程去抢占锁
        new Thread(new LockThread()).start();
        System.out.println("===========轻量级锁===========");
        System.out.println(ClassLayout.parseInstance(object).toPrintable());

        //多个线程抢占,一直在自旋,升级位重量级锁
        for (int i = 0; i < 20; i++) {
            new Thread(new LockThread()).start();
        }
        System.out.println("===========重量级锁===========");
        System.out.println(ClassLayout.parseInstance(object).toPrintable());



    }

}

结果:

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值