synchronized锁升级过程

本文详细介绍了Java中锁的升级机制,包括偏向锁、轻量级锁和重量级锁,以及它们在内存布局中的体现。通过实例代码展示了锁升级的过程,并讨论了何时从偏向锁升级到轻量级锁,以及轻量级锁升级到重量级锁的条件。同时,分析了开启偏向锁的效率问题,并解释了对象在内存中的内存布局结构。
摘要由CSDN通过智能技术生成

一.锁升级理论.

在synchronized锁升级过程中涉及到以下几种锁.先说一下这几种锁是什么意思.

偏向锁:只有一个线程争抢锁资源的时候.将线程拥有者标识为当前线程.
轻量级锁(自旋锁):一个或多个线程通过CAS去争抢锁,如果抢不到则一直自旋.
重量级锁:多个线程争抢锁,向内核申请锁资源,将未争抢成功的锁放到队列中直接阻塞.

为什么要有锁的升级过程?
      在最开始的时候,其实就是无锁直接到重量级锁,但是重量级锁需要向内核申请额外的锁资源,这就涉及到用户态和内核态的转换,比较浪费资源,而且大多数情况下,其实还是一个线程去争抢锁,完全不需要重量级锁.

锁的具体升级过程(通常情况下):

  1. 当只有一个线程去争抢锁的时候,会先使用偏向锁,就是给一个标识,说明现在这个锁被线程a占有.
  2. 后来又来了线程b,线程c,说凭什么你占有锁,需要公平的竞争,于是将标识去掉,也就是撤销偏向锁,升级为轻量级锁,三个线程通过CAS进行锁的争抢(其实这个抢锁过程还是偏向于原来的持有偏向锁的线程).
  3. 现在线程a占有了锁,线程b,线程c一直在循环尝试获取锁,后来又来了十个线程,一直在自旋,那这样等着也是干耗费CPU资源,所以就将锁升级为重量级锁,向内核申请资源,直接将等待的线程进行阻塞.

锁升级的过程如下所示:

在这里插入图片描述

什么情况下偏向锁才会升级为轻量级锁,什么时候轻量级锁才会升级为重量级锁?

  1. 只有一个线程的时候就是偏向锁(当偏向锁开启的时候,偏向锁默认开启),当争抢的线程超过一个,升级为轻量级锁.
  2. 当自旋的线程循环超过10次,或者线程等待的数量超过cpu的1/2,升级为重量级锁.其实轻量级锁就适用于那种执行任务很短的线程,可能通过一两次自旋,就能够获取到锁.

开启偏向锁一定比轻量级锁高效吗?
      不一定,比如在一开始已经知道某个资源就需要被多个线程争抢,此时就不需要开启偏向锁,因为偏向锁给了标识之后,还需要取消这个标识,重新抢锁,比如在JVM中,偏向锁默认是延迟4秒才开始的,因为JVM在启动的时候需要多个线程竞争资源,并且这个都是一开始知道的.

二.对象在内存中的内存布局

在堆中,一个对象会包含以下四个部分:
在这里插入图片描述

我们如果使用synchronized对某个对象进行加锁,就会体现在mark word区域.在最低两个字节加以标识.

如下如所示:
重量级锁 对应 10
轻量级锁 对应 00
无锁和偏向锁都对应01,这个时候需要倒数第三个字节加以区分,
即 无锁 对应 001, 偏向锁 对应 101

在这里插入图片描述

三.实操,用代码演示锁升级过程

在之前大概讲述了锁升级的过程和一些基础理论,现在用实际代码演示一下锁的升级过程.

1.首先这里引入一个api,通过这个可以显示出对象的内存的信息.
在这里插入图片描述
演示:
定义一个简单类,这个类有一个成员变量test.然后我们输出这个对象的内存信息.
在这里插入图片描述
输出信息如下:
在这里插入图片描述
2.使用程序演示锁升级过程
如下,给定一个锁对象,然后依次用一个线程,两个线程,多个线程去争抢锁,演示锁升级过程.

public class DemoApplication {
    static Object obj = new Object();
    //指定任务
     static class LockThread implements Runnable{
        @Override
        public void run() {
            synchronized (obj){
                //进行一些耗时操作
                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(obj).toPrintable());

        //两个线程抢占锁,升级为轻量级锁
        new Thread(new LockThread()).start();
        System.out.println("===========轻量级锁===========");
        System.out.println(ClassLayout.parseInstance(obj).toPrintable());

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

    }
}

输出结果如下,可以很直观的看到偏向锁–>轻量级锁–>重量级锁
在这里插入图片描述

其他注意点:
      并不是一定会有一个偏向锁->轻量级锁->重量级锁的过程,比如如果出现严重的耗时操作(sleep,或者wait等),就会直接由偏向锁升级为重量级锁.

  • 36
    点赞
  • 218
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序员bling

义父,感谢支持

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

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

打赏作者

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

抵扣说明:

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

余额充值