Java Synchronized字節(jié)碼層分析體驗
Synchronized是什么
各位Java讀者,對于synchronized關鍵字并不陌生,在各種中間件源碼或者JDK源碼中都能看到,對于不熟悉synchronized的讀者只知道在多線程中需要使用到synchronized關鍵字,知道synchronized能夠保證線程安全,那么本篇文章帶領各位讀者對synchronized有一個認識。
- 稱之為:互斥鎖(同時只能一個線程執(zhí)行,其他的線程將會等待)
- 又稱之為:悲觀鎖(同時只能一個線程執(zhí)行,其他的線程將會等待)
- JVM虛擬機幫你實現(xiàn),開發(fā)者只需要使用synchronized關鍵字即可。
- 使用時需要用一個對象當鎖的互斥量
- 能夠保證一段代碼(臨界區(qū))的原子性+可見性。
- 等等......
從字節(jié)碼層面解析Synchronized關鍵字
從案例入手,最合適不過。
class Demo1{ // 互斥對象 static Object object = new Object(); // 競爭條件 static int cout = 0; public static void main(String[] args) { // 互斥 synchronized(object){ // 以下是臨界區(qū) cout++; System.out.println("synchronized"); } } }
僅僅從Java代碼,我們并不能看出啥東西,而Java程序編譯后是字節(jié)碼文件,所以我們解析一遍字節(jié)碼
Constant pool:
#1 = Methodref #7.#26 // java/lang/Object."<init>":()V
#2 = Fieldref #8.#27 // Demo1.object:Ljava/lang/Object;
#3 = Fieldref #8.#28 // Demo1.cout:I
#4 = Fieldref #29.#30 // java/lang/System.out:Ljava/io/PrintStream;
#5 = String #31 // synchronized
#6 = Methodref #32.#33 // java/io/PrintStream.println:(Ljava/lang/String;)V
#7 = Class #34 // java/lang/Object
#8 = Class #35 // Demo1
#9 = Utf8 object
#10 = Utf8 Ljava/lang/Object;
#11 = Utf8 cout
#12 = Utf8 I
#13 = Utf8 <init>
#14 = Utf8 ()V
#15 = Utf8 Code
#16 = Utf8 LineNumberTable
#17 = Utf8 main
#18 = Utf8 ([Ljava/lang/String;)V
#19 = Utf8 StackMapTable
#20 = Class #36 // "[Ljava/lang/String;"
#21 = Class #34 // java/lang/Object
#22 = Class #37 // java/lang/Throwable
#23 = Utf8 <clinit>
#24 = Utf8 SourceFile
#25 = Utf8 Demo1.java
#26 = NameAndType #13:#14 // "<init>":()V
#27 = NameAndType #9:#10 // object:Ljava/lang/Object;
#28 = NameAndType #11:#12 // cout:I
#29 = Class #38 // java/lang/System
#30 = NameAndType #39:#40 // out:Ljava/io/PrintStream;
#31 = Utf8 synchronized
#32 = Class #41 // java/io/PrintStream
#33 = NameAndType #42:#43 // println:(Ljava/lang/String;)V
#34 = Utf8 java/lang/Object
#35 = Utf8 Demo1
#36 = Utf8 [Ljava/lang/String;
#37 = Utf8 java/lang/Throwable
#38 = Utf8 java/lang/System
#39 = Utf8 out
#40 = Utf8 Ljava/io/PrintStream;
#41 = Utf8 java/io/PrintStream
#42 = Utf8 println
#43 = Utf8 (Ljava/lang/String;)V
0: getstatic #2 // 從2號常量池中拿到靜態(tài)變量,壓入到操作數(shù)棧中
3: dup // 把操作數(shù)棧棧頂?shù)膶ο筚x值一份
4: astore_1 // 將操作數(shù)棧的數(shù)據(jù)保存到1號局部變量表中,給釋放鎖使用
5: monitorenter // 互斥鎖開啟,也是synchronized的字節(jié)碼層面實現(xiàn)
6: getstatic #3 // 從2號常量池中拿到靜態(tài)變量,壓入到操作數(shù)棧中
9: iconst_1 // 將常量1壓入到操作數(shù)棧中
10: iadd // 消耗兩個操作數(shù)棧的數(shù)據(jù),相加,然后壓入棧頂
11: putstatic #3 // 將操作數(shù)棧棧頂?shù)淖兞抠x值給3號常量池
14: getstatic #4 // 將4號常量池的對象壓入操作數(shù)棧
17: ldc #5 // 解析5號常量池的符號,拿到字符串常量"synchronized"
19: invokevirtual #6 // 執(zhí)行println函數(shù),消耗2個操作數(shù)棧
22: aload_1 // 將1號局部變量表的數(shù)據(jù)壓入操作數(shù)棧
23: monitorexit // 互斥鎖的結束,也是synchronized的字節(jié)碼層面實現(xiàn)
24: goto 32 // 跳轉到32行。
27: astore_2 // 可能存在異常,但是要需要釋放鎖,所以把異常對象放入2號局部變量表
28: aload_1 // 把1號局部變量表數(shù)據(jù)壓入操作數(shù)棧的棧頂,供monitorexit指令使用
29: monitorexit // 可能存在異常,但是要需要釋放鎖,不然死鎖了。
30: aload_2 // 把異常對象從2號局部變量表中壓入操作數(shù)棧的棧頂
31: athrow // 存在異常拋出
32: return // 函數(shù)返回
以上是字節(jié)碼全解,其實很簡單,最終Synchronized關鍵字解析成字節(jié)為monitorenter和monitorexit字節(jié)碼指令,然后每次執(zhí)行這2個字節(jié)碼指令前,把互斥對象壓入操作數(shù)棧供給monitorenter和monitorexit字節(jié)碼指令使用。
所以下一篇就是去Hotspot源碼中解析monitorenter和monitorexit字節(jié)碼指令的詳細流程。
Synchronized與ReentrantLock的區(qū)別
這是一道很常見的面試題,面試被問到的頻率非常高
相似點:
都是互斥鎖的實現(xiàn)
不同點:
- Synchronized基于JVM內部實現(xiàn),ReentrantLock基于Java層面實現(xiàn)(但是ReentrantLock核心代碼還是調用C++代碼)。
- Synchronized在1.6以后經(jīng)過優(yōu)化,存在幾個不同級別的鎖,根據(jù)線程競爭的力度提升鎖的力度(俗稱鎖升級),更多的適合場景,而ReentrantLock在鎖力度選擇上略顯死板。
- ReentrantLock雖然在鎖力度選擇上略顯死板,但是可以選擇公平和非公平,而Synchronized只能是非公平鎖
- ReentrantLock的條件等待隊列,可創(chuàng)建多個,高定制化。而Synchronized底層只有一個隊列。
- ReentrantLock需要用戶手動開啟鎖,手動釋放鎖。而Synchronized關鍵字底層通過字節(jié)碼自動實現(xiàn)
到此這篇關于Java Synchronized字節(jié)碼層分析體驗的文章就介紹到這了,更多相關Java Synchronized內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Springboot中如何使用Redisson實現(xiàn)分布式鎖淺析
redisson是redis的java客戶端程序,國內外很多公司都有在用,下面這篇文章主要給大家介紹了關于Springboot中如何使用Redisson實現(xiàn)分布式鎖的相關資料,需要的朋友可以參考下2021-10-10SpringBoot實現(xiàn)token登錄的示例代碼
在進行登錄驗證時,我們需要session或cookie會話進行驗證,當我們脫離瀏覽器用app等向服務端發(fā)請求就沒有session和cookie機制,這時我們就需要使用token令牌進行登錄驗證,本文就詳細的介紹一下,感興趣的可以了解一下2022-03-03springboot統(tǒng)一異常處理(返回json)并格式化異常
這篇文章主要介紹了springboot統(tǒng)一異常處理(返回json)并格式化異常,對spring boot的默認異常處理方式進行修改,要統(tǒng)一返回數(shù)據(jù)格式,優(yōu)雅的數(shù)據(jù)交互,優(yōu)雅的開發(fā)應用,需要的朋友可以參考下2023-07-07