从 class 文件 看 synchronize 锁膨胀过程(偏向锁 轻量级锁 自旋锁 重量级锁)
大家好,我是烤鴨:
? ? 前幾天看馬士兵老師的并發(fā)的課,里邊講到了 synchronize 鎖的膨脹過(guò)程,今天想用代碼演示一下。
1.? 簡(jiǎn)單介紹
? ? 關(guān)于synchronize jdk 1.5 以后的優(yōu)化,由重量級(jí)鎖調(diào)整為膨脹過(guò)程。分別是?偏向鎖 輕量級(jí)鎖(自旋鎖)?重量級(jí)鎖。
2.? 用例編寫(xiě)
pom文件增加 jol的包,用于看對(duì)象頭的信息。
<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core --> <dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-core</artifactId><version>0.10</version> </dependency>下面的注釋已經(jīng)寫(xiě)的挺清楚的了,關(guān)于鎖幾種狀態(tài)的轉(zhuǎn)換。?
SyncSourceTest.java
package src.source;import org.openjdk.jol.info.ClassLayout;/*** Created by test on 2020/5/10*/ public class SyncSourceTest {static Object noLock;static Object biaseLock;static Object lightLock;static Object heavyLock;public static void main(String[] args) throws InterruptedException {noLock = new Object();// 無(wú)鎖狀態(tài),由于print 方法是synchronize 修飾,其實(shí)打印語(yǔ)句就已經(jīng)是加偏向鎖了(如果滿足下面的偏向鎖條件)System.out.print("線程["+Thread.currentThread().getName()+"]:無(wú)鎖狀態(tài)對(duì)象布局:"+ClassLayout.parseInstance(noLock).toPrintable());// 偏向鎖,由于JVM 默認(rèn)偏向鎖4s后開(kāi)啟,可以線程sleep.5 或者設(shè)置VM參數(shù)關(guān)閉延遲 -XX:BiasedLockingStartupDelay=0Thread.sleep(5000L);biaseLock = new Object();System.out.println("線程["+Thread.currentThread().getName()+"]:偏向鎖狀態(tài)對(duì)象布局:"+ClassLayout.parseInstance(biaseLock).toPrintable());// 輕量級(jí)鎖,由于輕量級(jí)鎖是偏向鎖升級(jí)的,需要先給對(duì)象一個(gè)偏向鎖,如果不加偏向鎖,只有一個(gè)線程加鎖變成偏向鎖lightLock = new Object();synchronized (lightLock) {System.out.println("線程["+Thread.currentThread().getName()+"]:[輕量級(jí)鎖提前加偏向鎖]輕量級(jí)鎖狀態(tài)對(duì)象布局:"+ClassLayout.parseInstance(lightLock).toPrintable());}synLight();// 重量級(jí)鎖heavyLock = new Object();synHeavy();}public static void synLight() throws InterruptedException {for (int i = 0; i < 1; i++) {getLightLock();}}public static void getLightLock() {new Thread(() -> {try {synchronized (lightLock){System.out.println("線程["+Thread.currentThread().getName()+"]:輕量級(jí)鎖狀態(tài)對(duì)象布局:"+ClassLayout.parseInstance(lightLock).toPrintable());}} catch (Exception e) {e.printStackTrace();}}).start();}public static void synHeavy() throws InterruptedException {for (int i = 0; i < 2; i++) {getHeavyLock();}}private static void getHeavyLock() {new Thread(() -> {try {synchronized (heavyLock){System.out.println("線程["+Thread.currentThread().getName()+"]:重量級(jí)鎖狀態(tài)對(duì)象布局:"+ClassLayout.parseInstance(heavyLock).toPrintable());}} catch (Exception e) {e.printStackTrace();}}).start();} }關(guān)于對(duì)象布局,我們就先不研究了,這里重點(diǎn)說(shuō)一下 對(duì)象頭。
HotSpot虛擬機(jī)的對(duì)象頭(Object Header)分為兩部分,第一 部分用于存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù),如哈希碼(HashCode)、GC分代年齡(Generational GC Age) 等。這部分?jǐn)?shù)據(jù)的長(zhǎng)度在32位和64位的Java虛擬機(jī)中分別會(huì)占用32個(gè)或64個(gè)比特,官方稱它為“Mark Word”。這部分是實(shí)現(xiàn)輕量級(jí)鎖和偏向鎖的關(guān)鍵。另外一部分用于存儲(chǔ)指向方法區(qū)對(duì)象類型數(shù)據(jù)的指 針,如果是數(shù)組對(duì)象,還會(huì)有一個(gè)額外的部分用于存儲(chǔ)數(shù)組長(zhǎng)度。 運(yùn)行之后我們看一下輸出信息: 線程[main]:無(wú)鎖狀態(tài)對(duì)象布局:java.lang.Object object internals:OFFSET SIZE TYPE DESCRIPTION VALUE0 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) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 線程[main]:偏向鎖狀態(tài)對(duì)象布局:java.lang.Object object internals:OFFSET SIZE TYPE DESCRIPTION VALUE0 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) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total線程[main]:[輕量級(jí)鎖提前加偏向鎖]輕量級(jí)鎖狀態(tài)對(duì)象布局:java.lang.Object object internals:OFFSET SIZE TYPE DESCRIPTION VALUE0 4 (object header) 05 28 e3 02 (00000101 00101000 11100011 00000010) (48441349)4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total線程[Thread-2]:重量級(jí)鎖狀態(tài)對(duì)象布局:java.lang.Object object internals:OFFSET SIZE TYPE DESCRIPTION VALUE0 4 (object header) ba ee 0d 26 (10111010 11101110 00001101 00100110) (638447290)4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total線程[Thread-0]:輕量級(jí)鎖狀態(tài)對(duì)象布局:java.lang.Object object internals:OFFSET SIZE TYPE DESCRIPTION VALUE0 4 (object header) 88 f5 a4 29 (10001000 11110101 10100100 00101001) (698676616)4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total線程[Thread-1]:重量級(jí)鎖狀態(tài)對(duì)象布局:java.lang.Object object internals:OFFSET SIZE TYPE DESCRIPTION VALUE0 4 (object header) ba ee 0d 26 (10111010 11101110 00001101 00100110) (638447290)4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes totalProcess finished with exit code 0可以看出 無(wú)鎖狀態(tài)下各個(gè)狀態(tài)下 mark word 后三位的值:
無(wú)鎖:001
偏向:101
輕量級(jí)鎖:000
重量級(jí)鎖:010
本來(lái)想寫(xiě)一下,關(guān)于 輕量級(jí)鎖 當(dāng)前線程棧幀中 lock record 和 mark word的變化,無(wú)奈查了很多資料不知道在哪可以看到 lock record,有的說(shuō)是顯式或者隱式地創(chuàng)建lock record 空間,咱也不清楚了。更多關(guān)于輕量級(jí)鎖的源碼看這篇吧。
https://blog.csdn.net/z69183787/article/details/104502540?utm_source=app
3. 編譯class文件
看下面的class文件,順便說(shuō)一下jvm的字節(jié)碼指令。
可以看下 class文件里邊對(duì)象的變化:
?7: putstatic ? ? #3 ? ? ? ? ? ? ? ? ?// Field noLock:Ljava/lang/Object; 靜態(tài)變量 初始化時(shí)
39: getstatic ? ? #3 ? ? ? ? ? ? ? ? ?// Field noLock:Ljava/lang/Object; 獲取?靜態(tài)變量
而到了偏向鎖對(duì)象初始化之前,線程 睡眠了5秒。
57: ldc2_w ? ? ? ?#16? ? ? ? ? ? ? ? ? ?// long 5000l ,5000入棧
60: invokestatic ?#18 ? ? ? ? ? ? ? ? // Method java/lang/Thread.sleep:(J)V , 執(zhí)行 sleep
135: monitorenter 對(duì)應(yīng)的這行代碼 :synchronized (lightLock)
184: monitorexit? 加鎖結(jié)束
190: monitorexit? 后面又有一次 加鎖結(jié)束
原因是 線程內(nèi)部加鎖后,調(diào)用 print 方法,又加了一次鎖(重入鎖),所以需要釋放兩次。
D:\gitee\rep\leetcode-gradle\src\main\java\src\source> javac -classpath ".;D:\dev\repository\org\openjdk\jol\jol-core\0.10\jol-core-0.10.jar" -encoding UTF-8 .\SyncSourceTest.javaD:\gitee\rep\leetcode-gradle\src\main\java\src\source> javap -c .\SyncSourceTest.class Compiled from "SyncSourceTest.java" public class src.source.SyncSourceTest {static java.lang.Object noLock;static java.lang.Object biaseLock;static java.lang.Object lightLock;static java.lang.Object heavyLock;public src.source.SyncSourceTest();Code:0: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]) throws java.lang.InterruptedException;Code:0: new #2 // class java/lang/Object3: dup4: invokespecial #1 // Method java/lang/Object."<init>":()V7: putstatic #3 // Field noLock:Ljava/lang/Object;10: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;13: new #5 // class java/lang/StringBuilder16: dup17: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V20: ldc #7 // String 線程[22: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;25: invokestatic #9 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;28: invokevirtual #10 // Method java/lang/Thread.getName:()Ljava/lang/String;31: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;34: ldc #11 // String ]:無(wú)鎖狀態(tài)對(duì)象布局:36: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;39: getstatic #3 // Field noLock:Ljava/lang/Object;42: invokestatic #12 // Method org/openjdk/jol/info/ClassLayout.parseInstance:(Ljava/lang/Object;)Lorg/openjdk/jol/info/ClassLayout;45: invokevirtual #13 // Method org/openjdk/jol/info/ClassLayout.toPrintable:()Ljava/lang/String;48: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;51: invokevirtual #14 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;54: invokevirtual #15 // Method java/io/PrintStream.print:(Ljava/lang/String;)V57: ldc2_w #16 // long 5000l60: invokestatic #18 // Method java/lang/Thread.sleep:(J)V63: new #2 // class java/lang/Object66: dup67: invokespecial #1 // Method java/lang/Object."<init>":()V70: putstatic #19 // Field biaseLock:Ljava/lang/Object;73: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;76: new #5 // class java/lang/StringBuilder79: dup80: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V83: ldc #7 // String 線程[85: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;88: invokestatic #9 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;91: invokevirtual #10 // Method java/lang/Thread.getName:()Ljava/lang/String;94: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;97: ldc #20 // String ]:偏向鎖狀態(tài)對(duì)象布局:99: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;102: getstatic #19 // Field biaseLock:Ljava/lang/Object;105: invokestatic #12 // Method org/openjdk/jol/info/ClassLayout.parseInstance:(Ljava/lang/Object;)Lorg/openjdk/jol/info/ClassLayout;108: invokevirtual #13 // Method org/openjdk/jol/info/ClassLayout.toPrintable:()Ljava/lang/String;111: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;114: invokevirtual #14 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;117: invokevirtual #21 // Method java/io/PrintStream.println:(Ljava/lang/String;)V120: new #2 // class java/lang/Object123: dup124: invokespecial #1 // Method java/lang/Object."<init>":()V127: putstatic #22 // Field lightLock:Ljava/lang/Object;130: getstatic #22 // Field lightLock:Ljava/lang/Object;133: dup134: astore_1135: monitorenter136: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;139: new #5 // class java/lang/StringBuilder142: dup143: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V146: ldc #7 // String 線程[148: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;151: invokestatic #9 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;154: invokevirtual #10 // Method java/lang/Thread.getName:()Ljava/lang/String;157: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;160: ldc #23 // String ]:[輕量級(jí)鎖提前加偏向鎖]輕量級(jí)鎖狀態(tài)對(duì)象布局:162: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;165: getstatic #22 // Field lightLock:Ljava/lang/Object;168: invokestatic #12 // Method org/openjdk/jol/info/ClassLayout.parseInstance:(Ljava/lang/Object;)Lorg/openjdk/jol/info/ClassLayout;171: invokevirtual #13 // Method org/openjdk/jol/info/ClassLayout.toPrintable:()Ljava/lang/String;174: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;177: invokevirtual #14 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;180: invokevirtual #21 // Method java/io/PrintStream.println:(Ljava/lang/String;)V183: aload_1184: monitorexit185: goto 193188: astore_2189: aload_1190: monitorexit191: aload_2192: athrow193: invokestatic #24 // Method synLight:()V196: new #2 // class java/lang/Object199: dup200: invokespecial #1 // Method java/lang/Object."<init>":()V203: putstatic #25 // Field heavyLock:Ljava/lang/Object;206: invokestatic #26 // Method synHeavy:()V209: returnException table:from to target type136 185 188 any188 191 188 anypublic static void synLight() throws java.lang.InterruptedException;Code:0: iconst_01: istore_02: iload_03: iconst_14: if_icmpge 167: invokestatic #27 // Method getLightLock:()V10: iinc 0, 113: goto 216: returnpublic static void getLightLock();Code:0: new #28 // class java/lang/Thread3: dup4: invokedynamic #29, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable;9: invokespecial #30 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V12: invokevirtual #31 // Method java/lang/Thread.start:()V15: returnpublic static void synHeavy() throws java.lang.InterruptedException;Code:0: iconst_01: istore_02: iload_03: iconst_24: if_icmpge 167: invokestatic #32 // Method getHeavyLock:()V10: iinc 0, 113: goto 216: return }4.? 總結(jié)
有很多文章對(duì) synchronize 分析過(guò),我這里只是想使用代碼演示一下各種場(chǎng)景,很多地方并沒(méi)有深入到源碼和原理層面。
簡(jiǎn)單來(lái)說(shuō),就是:
無(wú)鎖:mark word 記錄 hashcode和分代年齡。
單線程加鎖(偏向鎖),mark word 記錄線程id。
偏向鎖升級(jí)到 輕量級(jí)鎖,mark word 值 替換為 當(dāng)前線程棧中的lock record 的指針。
輕量級(jí)鎖到重量級(jí)鎖:mark word 值 重量級(jí)鎖?的指針。
其中自旋鎖是?輕量級(jí)鎖到重量級(jí)鎖 發(fā)生的:
用CAS操作嘗試把對(duì)象的Mark Word更新為指向Lock Record的指針,如果成功了,就是輕量級(jí)鎖,失敗了,就變成重量級(jí)鎖。?
總結(jié)
以上是生活随笔為你收集整理的从 class 文件 看 synchronize 锁膨胀过程(偏向锁 轻量级锁 自旋锁 重量级锁)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 算法引论:一种创造性方法(书)
- 下一篇: 程序日志--要养成好习惯