JVM synchronized鎖實(shí)現(xiàn)原理珊拼,看完還不懂算我輸J成搿!澎现!

# 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)載請注明出處

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末商蕴,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子芝发,更是在濱河造成了極大的恐慌绪商,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辅鲸,死亡現(xiàn)場離奇詭異格郁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)独悴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進(jìn)店門例书,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人刻炒,你說我怎么就攤上這事决采。” “怎么了坟奥?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵树瞭,是天一觀的道長。 經(jīng)常有香客問我爱谁,道長晒喷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任访敌,我火速辦了婚禮凉敲,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘寺旺。我一直安慰自己爷抓,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布阻塑。 她就那樣靜靜地躺著废赞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪叮姑。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天据悔,我揣著相機(jī)與錄音传透,去河邊找鬼。 笑死极颓,一個胖子當(dāng)著我的面吹牛朱盐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播菠隆,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼兵琳,長吁一口氣:“原來是場噩夢啊……” “哼狂秘!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起躯肌,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤者春,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后清女,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體钱烟,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年嫡丙,在試婚紗的時候發(fā)現(xiàn)自己被綠了拴袭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡曙博,死狀恐怖拥刻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情父泳,我是刑警寧澤般哼,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站尘吗,受9級特大地震影響逝她,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜睬捶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一黔宛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧擒贸,春花似錦臀晃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至座韵,卻和暖如春险绘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背誉碴。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工宦棺, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人黔帕。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓代咸,卻偏偏與公主長得像,于是被迫代替她去往敵國和親成黄。 傳聞我的和親對象是個殘疾皇子呐芥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評論 2 355