# JVM synchronized鎖實(shí)現(xiàn)原理仅胞,看完還不懂算我輸!N敉贰饼问!
> 本文將用代碼帶大家一探究竟,看看JVM究竟是如何實(shí)現(xiàn)線程同步以及鎖的升級過程, jdk版本為`HotSpot 64bit 1.8.0_91`, Talking is cheap, show me the code!!!
## 一揭斧、synchronized關(guān)鍵字的實(shí)現(xiàn)
> 我們將一段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");
? ? ? ? }
? ? }
}
/*
? ? 首先執(zhí)行javac TestMonitor.java編譯
? ? 再執(zhí)行 javap -c -s TestMonitor.class進(jìn)行反編譯
? ? 結(jié)果如下:
? ? 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對象在內(nèi)存中的存儲布局
> 在講鎖實(shí)現(xiàn)之前盅视,我們需要了解java對象的布局,可以使用open-jdk的jol包旦万,全稱Java object layout闹击,可以看到j(luò)ava對象在內(nèi)存中的布局,maven地址:
```xml
<dependency>
? ? <groupId>org.openjdk.jol</groupId>
? ? <artifactId>jol-core</artifactId>
? ? <version>0.10</version>
</dependency>
```
#### 我們看看下面程序運(yùn)行結(jié)果
```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());
? ? }
}
/**運(yùn)行結(jié)果
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個字節(jié)
2. klass pointer (類型指針)淆两,開啟指針壓縮并且JVM內(nèi)存小于32G時占用4個字節(jié)断箫,未開啟指針壓縮或者JVM內(nèi)存大于32G占用8個字節(jié)
3. 數(shù)組長度(數(shù)組對象才有),占用4個字節(jié)
4. 成員變量數(shù)據(jù)(基本類型存的數(shù)據(jù),對象類型存的引用指針)
5. padding(對齊秋冰,如果對象大小不是8的整數(shù)倍將補(bǔ)齊)
## 三仲义、synchronized鎖升級過程
> 鎖信息存放在對象的markword中,下面是markword的結(jié)構(gòu)
```text
|--------------------------------------------------------------------------------|--------------------|
|? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Mark Word (64 bits)? ? ? ? ? ? ? ? ? ? ? ? ? |? ? ? 鎖狀態(tài) ? |
|--------------------------------------------------------------------------------|--------------------|
| 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 | 重量級鎖 ? |
|--------------------------------------------------------------------------------|--------------------|
```
#### 對象一共有五個狀態(tài)
鎖狀態(tài) | markword標(biāo)志位
:---:|:---:
無鎖 | 001
偏向鎖 | 101
輕量級鎖(自旋鎖)| 00
重量級鎖 | 10
標(biāo)記GC | 11
代碼演示標(biāo)示位變化
```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));
? ? }
}
/*
? ? 運(yùn)行結(jié)果:
? ? ---------------無鎖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-----------------
*/
```
> 注意在運(yùn)行時需要加上JVM參數(shù)-XX:BiasedLockingStartupDelay=0,這個參數(shù)代表啟動時就開啟偏向鎖.JVM默認(rèn)會延遲幾秒鐘開啟偏向鎖剑勾,是因?yàn)镴VM在啟動時會有多線程鎖競爭埃撵,而在偏向鎖升級到輕量級鎖時會有一系列的復(fù)雜過程,所以在明知道會有競爭的情況下干脆不啟用偏向鎖虽另。
我們可以看到markword第6-8位的打印如下:
`101 -> 101 -> 000 -> 010 -> 010`
為什么沒有看到無鎖的001狀態(tài)暂刘?
因?yàn)橐坏╅_啟了偏向鎖后,創(chuàng)建對象默認(rèn)會是匿名偏向狀態(tài)洲赵,此時markword的threadId為空鸳惯,001狀態(tài)大家可以在上一個程序中看到
#### 轉(zhuǎn)載請注明出處