synchronized锁升级原理

简介: synchronized锁升级原理

Java对象的内存结构


对象内存结构


在64位操作系统下,


MarkWord(下图_mark)占64位


KlassWord(下图_klass)占32位   64位系统的Klass Word不是32位,默认64位,开启指针压缩后为32(感谢评论老哥的指出)


64位系统的Klass Word不是32位,默认64位,开启指针压缩后为32


_lengh(只有数据对象才有,不考虑)


实例数据(下图instance data)看参数的类型,int就占32位(4byte)


补齐(padding)是JVM规定java对象内存必须是8byte的倍数,如果实例数据占2byte,那么(64bit的Markword+32bit的Klassword+实例数据32bit)=128bit=16byte是8byte的倍数,所以padding部分为0。


6.png


查看对象内存结构


JDK8


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


public class SynchronizedDemo {
  public static void main(String[] args) {
    Dog dog = new Dog();
    System.out.println(ClassLayout.parseInstance(dog).toPrintable());
  }
}
class Dog {
  char age;
}


7.png


如上图所示,对象头中的MarkWord占8byte,KlassWord占4个byte,实例属性age是char类型占2个byte,那么此时加起来为14byte,为了满足是8的倍数,要补充2个byte。

下图是当Dog对象里的age变为int时打印的结果,请自行对比。


8.png


对象头


下图是引自《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版) 周志明》中的一个图,下图是32操作系统下的对象头中的Mark Word(32位),Klass Word(32位),一共是64位。


64操作系统下,Mark Word的长度是64,在加Klass Word(32位),一共是96位,其实对象头长什么样其实不是本文的重点,本文的重点是验证锁升级的过程,所以我们只需要关注对象头中Mark Word的最后3位即可,如下图中的后3位。


9.png


锁升级的过程


微信截图_20230224221201.png


前提


由于大小端引起的问题,使得这里展示的高低位相反,如下图所示,所以我要关注的就是⑧位置的最后3位足矣


10.png


无锁状态


public class SynchronizedDemo {
  public static void main(String[] args) {
    Dog dog = new Dog();
    System.out.println(ClassLayout.parseInstance(dog).toPrintable());
  }
}


如下图所示,001表示的无锁状态并且不允许偏向 (其实默认是开启偏向的,只不过虚拟机后在运行后几秒才开启偏向锁)


11.png


使用下面的参数,如下图所示 ,会发现状态为101,表示无锁状态


-XX:BiasedLockingStartupDelay=0


12.png


由无锁状态---->偏向锁状态


单线程访问锁的时候,锁由无锁状态变为偏向锁状态。


// -XX:BiasedLockingStartupDelay=0
public class SynchronizedDemo {
  public static void main(String[] args) {
    Dog dog = new Dog();
    System.out.println(ClassLayout.parseInstance(dog).toPrintable());
    //上锁
    synchronized (dog){
      System.out.println(ClassLayout.parseInstance(dog).toPrintable());
    }
    System.out.println(ClassLayout.parseInstance(dog).toPrintable());
  }
}
class Dog {
  int age;
}


13.png


如上图所示,开始状态为101,为可偏向,无锁状态


上锁后状态是101,为可偏向,有锁状态


解锁后:状态为101,为可偏向,有锁状态


区别为:当线程给无锁状态的lock加锁时,会把线程ID存储到MarkWord中,即锁偏向于该ID的线程,偏向锁不会自动释放。


上面表格中2->3的过程。


偏向锁状态---->轻量级锁状态


多线程使用锁(不竞争,错开时间访问),锁由偏向锁状态变为轻量级锁状态


// -XX:BiasedLockingStartupDelay=0
public class SynchronizedDemo {
  public static void main(String[] args) {
    Dog dog = new Dog();
    System.out.println("初始状态:");
    System.out.println(ClassLayout.parseInstance(dog).toPrintable());
    new Thread(
            () -> {
              synchronized (dog) {
                System.out.println("hello world");
              }
            },
            "t1")
        .start();
    System.out.println("线程1释放锁后:");
    System.out.println(ClassLayout.parseInstance(dog).toPrintable());
    try {
      TimeUnit.SECONDS.sleep(3);
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
    }
    new Thread(
            () -> {
              synchronized (dog) {
                System.out.println("线程2上锁:");
                System.out.println(ClassLayout.parseInstance(dog).toPrintable());
              }
              System.out.println("线程2释放锁:");
              System.out.println(ClassLayout.parseInstance(dog).toPrintable());
            },
            "t2")
        .start();
  }
}
class Dog {
  int age;
}


14.png


初始状态为101,为可偏向,并且为无锁状态

线程1释放锁后,状态为101,并且存储了线程ID,为偏向锁状态,偏向于线程1

线程2上锁,上锁后,状态为00,轻量级锁状态

线程2释放锁后,状态为001,此时为不可偏向的无锁状态。


重量级锁状态


// -XX:BiasedLockingStartupDelay=0
public class SynchronizedDemo {
  public static void main(String[] args) {
    Dog dog = new Dog();
    System.out.println("初始状态:");
    System.out.println(ClassLayout.parseInstance(dog).toPrintable());
    new Thread(
            () -> {
              synchronized (dog) {
                System.out.println("");
                try {
                  TimeUnit.SECONDS.sleep(3);
                } catch (Exception e) {
                  e.printStackTrace();
                } finally {
                }
              }
            },
            "t1")
        .start();
    new Thread(
            () -> {
              synchronized (dog) {
                System.out.println("线程2上锁");
                System.out.println(ClassLayout.parseInstance(dog).toPrintable());
                try {
                  TimeUnit.SECONDS.sleep(3);
                } catch (Exception e) {
                  e.printStackTrace();
                } finally {
                }
              }
              System.out.println("线程2释放锁");
              System.out.println(ClassLayout.parseInstance(dog).toPrintable());
            },
            "t2")
        .start();
  }
}
class Dog {
  int age;
}


15.png


如上图所示,锁初始状态为101,可偏向无锁状态

当线程1在使用锁,而线程2去上锁的时候,状态已经变为010,不可偏向重量级锁。


总结


单线程使用锁的时候为偏向锁。

多线程无竞争(错峰使用锁)的时候为轻量级锁。

有竞争的时候为重量级锁。

目录
相关文章
|
2月前
|
存储 缓存 安全
关于Synchronized锁升级,你该了解这些
关于Synchronized锁升级,你该了解这些
77 3
|
3月前
|
存储 安全 Java
Synchronized锁工作原理
Synchronized锁工作原理
|
8月前
|
Java 编译器
Java多线程(4)---死锁和Synchronized加锁流程
Java多线程(4)---死锁和Synchronized加锁流程
48 0
|
5月前
|
Java 编译器 程序员
synchronized 原理(锁升级、锁消除和锁粗化)
synchronized 原理(锁升级、锁消除和锁粗化)
|
5月前
|
存储 安全 Java
12.synchronized的锁重入、锁消除、锁升级原理?无锁、偏向锁、轻量级锁、自旋、重量级锁
12.synchronized的锁重入、锁消除、锁升级原理?无锁、偏向锁、轻量级锁、自旋、重量级锁
38 0
12.synchronized的锁重入、锁消除、锁升级原理?无锁、偏向锁、轻量级锁、自旋、重量级锁
|
6月前
|
存储 Java
面试~Synchronized 与 锁升级
面试~Synchronized 与 锁升级
37 0
|
10月前
|
安全 Java
锁升级原理
锁升级是指在多线程环境下,当一个线程持有了低级别的锁(如偏向锁或轻量级锁)时,如果有其他线程也要获取这个锁,那么就需要将锁升级为重量级锁。这样可以保证在并发情况下,多个线程之间的互斥访问。
163 1
|
10月前
|
存储 SpringCloudAlibaba 安全
JUC并发编程(四):synchronized底层原理和锁升级优化
`synchronized`翻译过来是**同步**的意思,它是Java中一个关键字,是JVM层面提供的同步锁机制,用于保证多线程访问同一资源的可见性、互斥性。即当一个线程已经获取资源锁时,其他试图获取的线程只能等待或者阻塞在那里。
68 0
JUC并发编程(四):synchronized底层原理和锁升级优化
|
12月前
|
Java 编译器
synchronized关键字(作用 + 特点 + 锁升级 + 锁优化 + 与 volatile 对比)
1. synchronized 的作用 1)保证原子性 2)保证内存可见性 3)保证有序性 2. synchronized 特点 3. 锁升级的过程 1)偏向锁 2)轻量级锁 3)重量级锁 4. 锁的优化操作 1)锁消除 2)锁粗化 5. synchronized 使用示例 1)修饰普通方法:锁当前实例对象 2)修饰静态方法:锁当前类对象 3)修饰代码块:指定锁哪个对象 6. volatile 的作用 1)保证内存可见性 2)保证有序性 7. synchronized 和 volatile 的区别
77 0
synchronized关键字(作用 + 特点 + 锁升级 + 锁优化 + 与 volatile 对比)
|
缓存 Java 数据库
synchronized锁升级的过程
之前只是了解过一些悲观锁的底层原理,和他具体是如何锁住线程的一些细节,正好今天休息,结合一些文章和自己的实践操作,整理成了一篇关于synchronized锁升级的过程,希望能对大家有所帮助.
151 0
synchronized锁升级的过程