当前位置 : 主页 > 网络编程 > PHP >

synchronized锁升级原理

来源:互联网 收集:自由互联 发布时间:2023-10-08
Java对象的内存结构 对象内存结构 在64位操作系统下, MarkWord(下图_mark)占64位 KlassWord(下图_klass)占32位 64位系统的Klass Word不是32位,默认64位,开启指针压缩后为32(感谢评论老哥的指


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。

synchronized锁升级原理_后端

查看对象内存结构

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;
}

 

synchronized锁升级原理_后端_02

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

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

synchronized锁升级原理_sed_03

对象头

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

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

synchronized锁升级原理_无锁_04

锁升级的过程

锁状态

25bit

4bit

1bit

2bit

23bit

2bit

是否偏向锁

锁标志位

1

无锁

对象的HashCode

分代年龄

0

01

2

无锁

对象的HashCode

分代年龄

1

01

3

偏向锁

线程ID

Epoch

分代年龄

1

01

4

轻量级锁

指向栈中锁记录的指针

00

5

重量级锁

指向重量级锁的指针

10

6

GC标记

11

前提

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

synchronized锁升级原理_无锁_05

无锁状态

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

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

synchronized锁升级原理_后端_06

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

-XX:BiasedLockingStartupDelay=0

synchronized锁升级原理_无锁_07

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

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

// -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;
}

 

synchronized锁升级原理_开发语言_08

如上图所示,开始状态为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;
}

synchronized锁升级原理_开发语言_09

初始状态为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;
}

synchronized锁升级原理_后端_10

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

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

总结

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

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

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

参考

​​这可能是B站上最深入解析的synchronized底层原理解析_哔哩哔哩_bilibili​​

​​视频去哪了呢?_哔哩哔哩_bilibili​​


【文章由韩国大带宽服务器 http://www.558idc.com/lg.html处的文章,转载请说明出处】
上一篇:ogr2ogr矢量数据导入到mysql数据库
下一篇:没有了
网友评论