# JVM synchronized锁实现原理,看完还不懂算我输!!!
> 本文将用代码带大家一探究竟,看看JVM究竟是如何实现线程同步以及锁的升级过程, jdk版本为`HotSpot 64bit 1.8.0_91`, Talking is cheap, show me the code!!!
## 一、synchronized关键字的实现
> 我们将一段java代码通过`javap -c -s`命令反编译一下看看
```java
package com.ywzlp.demo;
/**
* Created by yuwei on 2020/6/6
*/
public class TestMonitor {
public static void main(String[] args) {
Object lock = new Object();
synchronized (lock) {
System.out.println("hello lock");
}
}
}
/*
首先执行javac TestMonitor.java编译
再执行 javap -c -s TestMonitor.class进行反编译
结果如下:
public class com.ywzlp.demo.TestMonitor {
public com.ywzlp.demo.TestMonitor();
descriptor: ()V
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
Code:
0: new #2 // class java/lang/Object
3: dup
4: invokespecial #1 // Method java/lang/Object."<init>":()V
7: astore_1
8: aload_1
9: dup
10: astore_2
11: monitorenter
12: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
15: ldc #4 // String hello lock
17: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
20: aload_2
21: monitorexit
22: goto 30
25: astore_3
26: aload_2
27: monitorexit
28: aload_3
29: athrow
30: return
Exception table:
from to target type
12 22 25 any
25 28 25 any
}
*/
```
我们可以看到,synchronized被编译后会生成两条指令,`monitorenter` 和 `monitorexit`,下面带大家看看这两条指令具体会做些什么操作
## 二、java对象在内存中的存储布局
> 在讲锁实现之前,我们需要了解java对象的布局,可以使用open-jdk的jol包,全称Java object layout,可以看到java对象在内存中的布局,maven地址:
```xml
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.10</version>
</dependency>
```
#### 我们看看下面程序运行结果
```java
package com.ywzlp.demo;
import org.openjdk.jol.info.ClassLayout;
/**
* Created by yuwei on 2020/6/6
*/
public class TestLock {
private int num;
private String str;
public static void main(String[] args) {
TestLock testLock = new TestLock();
System.out.println(ClassLayout.parseInstance(testLock).toPrintable());
}
}
/**运行结果
com.ywzlp.demo.TestLock object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
12 4 int TestLock.num 0
16 4 java.lang.String TestLock.str null
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
*
*/
```
#### 对象布局包含下面几个部分
1. markword(包含锁、GC、hashCode信息),占用8个字节
2. klass pointer (类型指针),开启指针压缩并且JVM内存小于32G时占用4个字节,未开启指针压缩或者JVM内存大于32G占用8个字节
3. 数组长度(数组对象才有),占用4个字节
4. 成员变量数据(基本类型存的数据,对象类型存的引用指针)
5. padding(对齐,如果对象大小不是8的整数倍将补齐)
## 三、synchronized锁升级过程
> 锁信息存放在对象的markword中,下面是markword的结构
```text
|--------------------------------------------------------------------------------|--------------------|
| Mark Word (64 bits) | 锁状态 |
|--------------------------------------------------------------------------------|--------------------|
| unused:25 | identity_hashcode:31 | cms_free:1 | age:4 | biased_lock:1 | lock:2 | 无锁 |
|--------------------------------------------------------------------------------|--------------------|
| thread:54 | epoch:2 | cms_free:1 | age:4 | biased_lock:1 | lock:2 | 偏向锁 |
|--------------------------------------------------------------------------------|--------------------|
| ptr_to_lock_record | lock:2 | 轻量级锁 |
|--------------------------------------------------------------------------------|--------------------|
| ptr_to_heavyweight_monitor | lock:2 | 重量级锁 |
|--------------------------------------------------------------------------------|--------------------|
```
#### 对象一共有五个状态
锁状态 | markword标志位
:---:|:---:
无锁 | 001
偏向锁 | 101
轻量级锁(自旋锁)| 00
重量级锁 | 10
标记GC | 11
代码演示标示位变化
```java
package com.ywzlp.demo;
import org.openjdk.jol.info.ClassLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* -XX:BiasedLockingStartupDelay=0
* Created by yuwei on 2020/6/6
*/
public class TestLock {
private int num;
private String str;
public static void main(String[] args) throws InterruptedException {
TestLock testLock = new TestLock();
System.out.println("---------------无锁boundary-----------------");
System.out.println(ClassLayout.parseClass(TestLock.class).toPrintable(testLock));
System.out.println("---------------无锁boundary-----------------");
System.out.println("---------------偏向锁boundary-----------------");
testLock.add(500);
System.out.println("---------------偏向锁boundary-----------------");
System.out.println("---------------轻量级锁boundary-----------------");
Thread t1 = new Thread(() -> testLock.add(500));
t1.start();
t1.join();
System.out.println("---------------轻量级锁boundary-----------------");
System.out.println("---------------重量级锁boundary-----------------");
ExecutorService executorService = Executors.newFixedThreadPool(2);
List<Future<?>> futureList = new ArrayList<>(2);
for (int i = 0; i < 2; i++) {
futureList.add(executorService.submit(() -> testLock.add(500)));
}
futureList.forEach(future -> {
try {
future.get();
} catch (Exception ignored) {}
});
executorService.shutdown();
futureList.clear();
System.out.println("---------------重量级锁boundary-----------------");
}
public synchronized void add(int timeToSleep) {
if (timeToSleep > 0) {
try {
Thread.sleep(timeToSleep);
} catch (InterruptedException ignored) {
}
}
num++;
System.out.println(ClassLayout.parseClass(TestLock.class).toPrintable(this));
}
}
/*
运行结果:
---------------无锁boundary-----------------
com.ywzlp.demo.TestLock object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
12 4 int TestLock.num 0
16 4 java.lang.String TestLock.str null
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
---------------无锁boundary-----------------
---------------偏向锁boundary-----------------
com.ywzlp.demo.TestLock object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 48 80 25 (00000101 01001000 10000000 00100101) (629164037)
4 4 (object header) fa 7f 00 00 (11111010 01111111 00000000 00000000) (32762)
8 4 (object header) 05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
12 4 int TestLock.num 1
16 4 java.lang.String TestLock.str null
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
---------------偏向锁boundary-----------------
---------------轻量级锁boundary-----------------
com.ywzlp.demo.TestLock object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) f0 f7 4b 07 (11110000 11110111 01001011 00000111) (122419184)
4 4 (object header) 00 70 00 00 (00000000 01110000 00000000 00000000) (28672)
8 4 (object header) 05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
12 4 int TestLock.num 2
16 4 java.lang.String TestLock.str null
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
---------------轻量级锁boundary-----------------
---------------重量级锁boundary-----------------
com.ywzlp.demo.TestLock object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 3a b1 82 26 (00111010 10110001 10000010 00100110) (646099258)
4 4 (object header) fa 7f 00 00 (11111010 01111111 00000000 00000000) (32762)
8 4 (object header) 05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
12 4 int TestLock.num 3
16 4 java.lang.String TestLock.str null
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
com.ywzlp.demo.TestLock object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 3a b1 82 26 (00111010 10110001 10000010 00100110) (646099258)
4 4 (object header) fa 7f 00 00 (11111010 01111111 00000000 00000000) (32762)
8 4 (object header) 05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
12 4 int TestLock.num 4
16 4 java.lang.String TestLock.str null
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
---------------重量级锁boundary-----------------
*/
```
> 注意在运行时需要加上JVM参数-XX:BiasedLockingStartupDelay=0,这个参数代表启动时就开启偏向锁.JVM默认会延迟几秒钟开启偏向锁,是因为JVM在启动时会有多线程锁竞争,而在偏向锁升级到轻量级锁时会有一系列的复杂过程,所以在明知道会有竞争的情况下干脆不启用偏向锁。
我们可以看到markword第6-8位的打印如下:
`101 -> 101 -> 000 -> 010 -> 010`
为什么没有看到无锁的001状态?
因为一旦开启了偏向锁后,创建对象默认会是匿名偏向状态,此时markword的threadId为空,001状态大家可以在上一个程序中看到
#### 转载请注明出处
网友评论