
上一節你了解了什么是CAS、synchronized形成的鎖的型別、重量級鎖是用戶態行程向內核態申請資源加鎖程序,HotSpot Java物件結構,以及初步從3個層面分析了下synchronized的核心流程,還記得核心流程圖么?
如下所示:

這一節我們仔細來分析下這個程序中,每一步的底層原理,我們需要用到一個工具包,JOL,它可以將java物件的資訊列印出來,你可以通過這個工具分析升級程序中鎖的標記變化,
synchronized鎖升級流程詳解
synchronized鎖升級流程詳解
首先是我們看一下:
-
偏向鎖未啟動:無鎖態 new - > 普通物件,
-
偏向鎖已啟動:無鎖態 new - > 匿名偏向鎖,
我們來看個例子: 設定JVM引數,-XX:BiasedLockingStartupDelay=10 環境:JDK1.8
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.10</version>
</dependency>
public class HelloSynchronized {
public static void main(String[] args) {
Object object = new Object();
System.out.println(ClassLayout.parseInstance(object).toPrintable());
synchronized (object){
}
}
}
輸出結果如下:
java.lang.Object 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) 00 10 00 00 (00000000 00010000 00000000 00000000) (4096)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
大端還是小端序? System.out.println(ByteOrder.nativeOrder()); 可以查看當前cpu的位元組序,輸出是LITTLE_ENDIAN意味著是小端序 l 小端序:資料的高位位元組存放在地址的高端 低位位元組存放在地址低端 l 大端序: 資料的高位位元組存放在地址的低端 低位位元組存放在地址高端 比如一個整形0x1234567 ,1是高位資料,7是低位資料,按照小端序01放在記憶體地址的高位,比如放在0x100 ,23就放在0x101以此類推,大端序反之,
如下圖:(圖片來源于網路)
可以看到OFFSET為0-4的Obejct header 的Value中 0 01這個標記,
也就是說,Object o = new Object() 默認的鎖 = 0 01 表示了無鎖態 注意:如果偏向鎖打開,默認是匿名偏向狀態,
可以修改JVM引數-XX:BiasedLockingStartupDelay=0,再次運行
java.lang.Object 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) 00 10 00 00 (00000000 00010000 00000000 00000000) (4096)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
可以看到OFFSET為0-4的Obejct header 的Value中 1 01這個標記,表示一個偏向鎖,為什么說是匿名的呢?因為在JVM底層C++代碼中,偏向鎖默認有一個C++變數JavaThread指標,使用54位記錄這個指標,從OFFSET為0-4的Obejct header 的Value中看到除了鎖的標記為是101外,其余都是0,表示沒有JavaThread指標無,所以是一個匿名偏向,
偏向鎖未啟動是指什么? 偏向鎖未啟動指默認情況 偏向鎖有個時延,默認是4秒(不同JDK版本可以不一樣) 可以通過一個JVM引數控制,-XX:BiasedLockingStartupDelay=4,因為JVM虛擬機自己有一些默認啟動的執行緒,里面有好多sync代碼,這些sync代碼啟動時就知道肯定會有競爭,如果使用偏向鎖,就會造成偏向鎖不斷的進行鎖撤銷和鎖升級的操作,效率較低,
所以這個2個流程的變化如下圖所示:

接著我們看往后看:
- 偏向鎖已啟動:無鎖態 new - > 匿名偏向鎖 - 》 偏向鎖
- 偏向鎖未啟動:無鎖態 new - > 普通物件 - 》 偏向鎖
當執行到同步代碼時候,有了明確的加鎖執行緒,所以我們增加一行日志,列印Object的物件頭資訊,會發現,已經發生如下變化:
public class HelloSynchronized {
public static void main(String[] args) {
Object object = new Object();
System.out.println(ClassLayout.parseInstance(object).toPrintable());
synchronized (object){
System.out.println(ClassLayout.parseInstance(object).toPrintable());
}
}
}
java.lang.Object 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) 00 10 00 00 (00000000 00010000 00000000 00000000) (4096)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 f8 ba 86 (00000101 11111000 10111010 10000110) (-2034567163)
4 4 (object header) b0 01 00 00 (10110000 00000001 00000000 00000000) (432)
8 4 (object header) 00 10 00 00 (00000000 00010000 00000000 00000000) (4096)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
可以看到OFFSET為0-4的Obejct header 的Value中 1 01這個標記之外,不在全部是0,說明已經不再是匿名偏向鎖了,
如果原來不是匿名偏向鎖,只是一個普通物件,進入synchronized代碼塊后,會直接變成偏向鎖,如下圖所示:

- 偏向鎖未啟動:無鎖態 new - > 普通物件 - > 輕量級鎖(自旋鎖)
接下來我們看一下,無鎖也有可能直接變成輕量級鎖,設定JVM引數,-XX:BiasedLockingStartupDelay=10,在synchronized內部加入JOL的列印輸出,就會列印如下物件資訊:
//-XX:BiasedLockingStartupDelay=10
public static void main(String[] args) {
Object object = new Object();
System.out.println(ClassLayout.parseInstance(object).toPrintable()); //new-普通物件 0 01
synchronized (object){
System.out.println(ClassLayout.parseInstance(object).toPrintable()); //new->輕量鎖 00
}
}
java.lang.Object 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) 00 10 00 00 (00000000 00010000 00000000 00000000) (4096)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) e0 f0 9f 70 (11100000 11110000 10011111 01110000) (1889530080)
4 4 (object header) 2a 00 00 00 (00101010 00000000 00000000 00000000) (42)
8 4 (object header) 00 10 00 00 (00000000 00010000 00000000 00000000) (4096)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
此流程如下圖所示:

- 偏向鎖->輕量級鎖(輕度競爭)
當有執行緒競爭鎖時,會撤銷偏向鎖,升級輕量級鎖,
//-XX:BiasedLockingStartupDelay=0
public static void main(String[] args) {
Object object = new Object();
System.out.println("初始化new");
System.out.println(ClassLayout.parseInstance(object).toPrintable()); //101+全是0 匿名偏向鎖
synchronized (object){
System.out.println(ClassLayout.parseInstance(object).toPrintable());//101+非0 偏向鎖
}
new Thread(()->{
try {
Thread.sleep(1000);
synchronized (object){
System.out.println("t執行緒獲取鎖");
System.out.println(ClassLayout.parseInstance(object).toPrintable()); //00 object被另一個執行緒加鎖,發生競爭,偏向鎖->輕量鎖
}
} catch (InterruptedException e) {}
}).start();
}
java.lang.Object 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) 00 10 00 00 (00000000 00010000 00000000 00000000) (4096)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 20 cc 74 (00000101 00100000 11001100 01110100) (1959534597)
4 4 (object header) b3 01 00 00 (10110011 00000001 00000000 00000000) (435)
8 4 (object header) 00 10 00 00 (00000000 00010000 00000000 00000000) (4096)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) d8 f3 df 89 (11011000 11110011 11011111 10001001) (-1981811752)
4 4 (object header) 46 00 00 00 (01000110 00000000 00000000 00000000) (70)
8 4 (object header) 00 10 00 00 (00000000 00010000 00000000 00000000) (4096)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
可以看到鎖的變化從匿名偏向->偏向->輕量鎖,這里簡單提下輕量鎖的底層原理:
當變成輕量鎖,如果有別的執行緒嘗試獲取鎖,會在執行緒在自己的執行緒堆疊生成LockRecord C++物件,用CAS操作將markword中62位地址,使用參考(C++叫指標)指向自己這個執行緒的對應的LR物件,如果設定成功者得到鎖,否則繼續CAS執行回圈自旋操作,(PS:輕量鎖的底層是使用一個LockRecord C++物件,偏向使用的是JavaThread這個物件指標)
整個升級流程如下圖所示:

- 偏向鎖->重量級鎖(重度競爭)
很早之前JDK判斷競爭加劇的條件是:有執行緒超過10次自旋(可以通過-XX:PreBlockSpin) 或者自旋執行緒數超過CPU核數的一半,但是1.6之后,加入自適應自旋 Adapative Self Spinning的機制,由JVM自己控制升級重量級鎖,
升級時,向作業系統申請資源,通過linux mutex申請互斥鎖 , CPU從3級到0級系統呼叫,執行緒掛起,進入等待佇列,等待作業系統的調度,然后再映射回用戶空間,
//-XX:BiasedLockingStartupDelay=0
public static void main(String[] args) {
System.out.println(ByteOrder.nativeOrder());
Object object = new Object();
System.out.println(ClassLayout.parseInstance(object).toPrintable()); //101+全是0 匿名偏向鎖
System.out.println("初始化new");
synchronized (object){
System.out.println(ClassLayout.parseInstance(object).toPrintable());//101+非0 偏向鎖
}
for(int i=0;i<10;i++){
new Thread(()->{
try {
Thread.sleep(1000);
synchronized (object){
System.out.println(Thread.currentThread().getName()+"執行緒獲取鎖");
System.out.println(ClassLayout.parseInstance(object).toPrintable()); //10 object被多個執行緒競爭 ,偏向鎖->重量鎖
}
} catch (InterruptedException e) {}
}).start();
}
}
java.lang.Object 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) 00 10 00 00 (00000000 00010000 00000000 00000000) (4096)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
// 初始化new
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 08 18 e4 (00000101 00001000 00011000 11100100) (-468187131)
4 4 (object header) 1f 02 00 00 (00011111 00000010 00000000 00000000) (543)
8 4 (object header) 00 10 00 00 (00000000 00010000 00000000 00000000) (4096)
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執行緒獲取鎖
java.lang.Object object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 02 8f 57 ff (00000010 10001111 01010111 11111111) (-11038974)
4 4 (object header) 1f 02 00 00 (00011111 00000010 00000000 00000000) (543)
8 4 (object header) 00 10 00 00 (00000000 00010000 00000000 00000000) (4096)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
上面的代碼可以看出,鎖升級是從匿名偏向鎖->偏向鎖->重量鎖的程序,JVM判斷出for回圈中創建了10個執行緒,競爭激烈,當執行緒獲取鎖的時候直接就是重量級鎖,如下圖所示:

最后一條線,輕量級鎖到重量級鎖的代碼我就不演示了,當競爭加劇的時候,輕量級鎖會升級為重量級鎖的,
好了,到這里相信你對synchronized的鎖升級流程已經理解的非常清楚了,接下來我們看一些鎖升級程序中的一些原理和細節,
鎖升級流程中的核心原理和細節
鎖升級流程中的核心原理和細節
既然synchronized的鎖機制和java物件頭的結構密切相關,物件頭中的markword有鎖標記,分代年齡,指標參考等含義,接下來就讓我們仔細分析下偏向鎖、自旋鎖、重量級鎖它們的底層原理和物件頭中的markword的聯系,
偏向鎖的基本原理

輕量鎖的C++實作機制和可重入性(基于堆疊)
輕量鎖的原理和偏向鎖類似,只不過markWord中的指標是一個LockRecord,并且修改指標的操作為CAS,那個執行緒CAS設定成功就會獲取鎖,如下圖所示:

synchronized的鎖是可重入的,這樣子類才可以呼叫父類的同步方法,不會出問題,使用同一個物件或者類也可以多次加synchronized的代碼塊,所以輕量鎖重入性的實作是基于入堆疊LR物件,來記錄重入次數的,如下所示:

重量鎖的C++實作機制和可重入性(基于ObjectMonitor類似于AQS)
重量級鎖的底層原理,是通過在Mark Word里就有一個指標,是指向了這個物件實體關聯的monitor物件的地址,這個monitor是c++實作的,不是java實作的,這個monitor實際上是c++實作的一個ObjectMonitor物件,里面包含了一個_owner指標,指向了持有鎖的執行緒,ObjectMonitor它的C++結構體如下:
// objectMonitor.hpp
ObjectMonitor() {
_header = NULL;
_count = 0; // 重入次數
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL; // 獲得鎖的執行緒
_WaitSet = NULL; // 呼叫wait()方法被阻塞的執行緒
_WaitSetLock = 0 ;
_Responsible = NULL
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ; // Contention List中那些有資格成為候選人的執行緒被移到Entry List
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
_previous_owner_tid = 0;
}
ObjectMonitor里還有一個entrylist,想要加鎖的執行緒全部先進入這個entrylist等待獲取機會嘗試加鎖,實際有機會加鎖的執行緒,就會設定_owner指標指向自己,然后對_count計數器累加1次,
各個執行緒嘗試競爭進行加鎖,此時競爭加鎖是在JDK 1.6以后優化成了基于CAS來進行加鎖,理解為跟之前的Lock API的加鎖機制是類似的,CAS操作,操作_count計數器,比如說將_count值嘗試從0變為1,
如果成功了,那么加鎖成功了count加1,修改成;如果失敗了,那么加鎖失敗了,就會進入waitSet等待,
然后釋放鎖的時候,先是對_count計數器遞減1,如果為0了就會設定_owner為null,不再指向自己,代表自己徹底釋放鎖,
如果獲取鎖的執行緒執行wait,就會將計數器遞減,同時_owner設定為null,然后自己進入waitset中等待喚醒,別人獲取了鎖執行類似notifyAll的時候就會喚醒waitset中的執行緒競爭嘗試獲取鎖,
整個程序如下所示:

可能你會問,那嘗試加鎖這個程序,也就是對_count計數器累加操作,是怎么執行的?如何保證多執行緒并發的原子性呢?
很簡單,這個地方count操作是一個類似于CAS的操作,
其實,你如果了解ReentrantLock底層的AQS機制,你就會發現,synchronized底層的實作和AQS差不多的,
只不過synchronized的底層是ObjectMonitor,它的地位就跟ReentrantLock里的AQS對應的實作Sync組件是差不多的,之后我們講到ReentrantLock的時候你就會發現了,
為什么有自旋鎖還需要重量級鎖?
自旋是消耗CPU資源的,如果鎖的時間長,或者自旋執行緒多,CPU會被大量消耗,
重量級鎖有等待佇列,所有拿不到鎖的進入等待佇列,不需要消耗CPU資源,
偏向鎖是否一定比自旋鎖效率高?
不一定,在明確知道會有多執行緒競爭的情況下,偏向鎖肯定會涉及鎖撤銷revoke,會消耗系統資源,所以,在鎖爭用特別激烈的時候,用偏向鎖未必效率高,還不如直接使用輕量級鎖(自旋鎖),
比如JVM啟動程序,會有很多執行緒競爭(已經明確),所以默認情況啟動時不打開偏向鎖,過一段兒時間再打開,
鎖消除
public void add(String str1,String str2){
StringBuffer sb = new StringBuffer();
sb.append(str1).append(str2);
}
我們都知道 StringBuffer 是執行緒安全的,因為它的關鍵方法都是被 synchronized 修飾過的,但我們看上面這段代碼,我們會發現,sb 這個參考只會在 add 方法中使用,不可能被其它執行緒參考(因為是區域變數,堆疊私有),因此 sb 是不可能共享的資源,JVM 會自動消除 StringBuffer 物件內部的鎖,
鎖粗化
public String test(String str){
int i = 0;
StringBuffer sb = new StringBuffer():
while(i < 100){
sb.append(str);
i++;
}
return sb.toString():
}
JVM 會檢測到這樣一連串的操作都對同一個物件加鎖(while 回圈內 100 次執行 append,沒有鎖粗化的就要進行 100 次加鎖/解鎖),此時 JVM 就會將加鎖的范圍粗化到這一連串的操作的外部(比如 while 虛幻體外),使得這一連串操作只需要加一次鎖即可,
wait和notify必須和sychronized一起使用!?
wait和notify必須和sychronized一起使用!?
wait和notify / notifyAll還是挺有用的,在多執行緒開發中和很多開源專案中,那么如何使用wait和notifyall呢?它們的作用主要是執行緒通信,所以某個執行緒可以用wait處于等待狀態,其他執行緒可以用notify來通知它,或者說是喚醒它,
wait與notify實作的一個底層原理其實和synchronized的重量級鎖原理類似,主要也是monitor物件,需要注意的是必須得對同一個物件實體進行加鎖,這樣的話,他們其實操作的才是通一個物件實體里的monitor相關的計數器、wait set,
換句話說,wait與notify,必須在synchronized代碼塊中使用,因為wait/notify底層都是C++代碼,是針對ObjectMonitor進行操作的,
舉個例子:
public static void main(String[] args) throws InterruptedException {
Object o = new Object();
Thread waitThread = new Thread(() -> {
try {
synchronized (o) {
System.out.println(Thread.currentThread().getName() + "執行緒獲取鎖,進行wait操作");
o.wait();
System.out.println(Thread.currentThread().getName() + "執行緒繼續執行,之后釋放了鎖");
}
} catch (InterruptedException e) {
}
});
waitThread.start();
Thread notifyThread =new Thread(()->{
try {
Thread.sleep(2000);
synchronized (o){
System.out.println(Thread.currentThread().getName()+"執行緒獲取鎖,執行notify喚醒操作");
o.notify();
System.out.println(Thread.currentThread().getName()+"執行緒繼續執行,之后釋放了鎖");
}
} catch (InterruptedException e) {}
});
notifyThread.start();
}
上面代碼的流程如下圖所示:

上面程序涉及很多細節,需要仔細研究HotSpot C++代碼,有興趣的同學可以研究下wait和notify/notifyAll的C++代碼,
大多情況下,核心還是掌握ObjectMonitor這個實作機制原理即可,你可能還有一些疑問,我找了一些wait和notify相關的常見的問題,供大家參考,
(以下轉載自:https://zhuanlan.zhihu.com/p/113851988),
為何要加synchronized鎖
從實作上來說,這個鎖至關重要,正因為這把鎖,才能讓整個wait/notify玩轉起來,當然我覺得其實通過其他的方式也可以實作類似的機制,不過hotspot至少是完全依賴這把鎖來實作wait/notify的,
wait方法執行后未退出同步塊,其他執行緒如何進入同步塊
這個問題其實要回答很簡單,因為在wait處理程序中會臨時釋放同步鎖,不過需要注意的是當某個執行緒呼叫notify喚起了這個執行緒的時候,在wait方法退出之前會重新獲取這把鎖,只有獲取了這把鎖才會繼續執行,想象一下,我們知道wait的方法是被monitorenter和monitorexit的指令包圍起來,當我們在執行wait方法程序中如果釋放了鎖,出來的時候又不拿鎖,那在執行到monitorexit指令的時候會發生什么?當然這可以做兼容,不過這實作起來還是很奇怪的,
為什么wait方法可能拋出nterruptedException例外
這個例外大家應該都知道,當我們呼叫了某個執行緒的interrupt方法時,對應的執行緒會拋出這個例外,wait方法也不希望破壞這種規則,因此就算當前執行緒因為wait一直在阻塞,當某個執行緒希望它起來繼續執行的時候,它還是得從阻塞態恢復過來,因此wait方法被喚醒起來的時候會去檢測這個狀態,當有執行緒interrupt了它的時候,它就會拋出這個例外從阻塞狀態恢復過來,
這里有兩點要注意:
如果被interrupt的執行緒只是創建了,并沒有start,那等他start之后進入wait態之后也是不能會恢復的
如果被interrupt的執行緒已經start了,在進入wait之前,如果有執行緒呼叫了其interrupt方法,那這個wait等于什么都沒做,會直接跳出來,不會阻塞
被notify(All)的執行緒有規律嗎
這里要分情況:
如果是通過notify來喚起的執行緒,那先進入wait的執行緒會先被喚起來
如果是通過nootifyAll喚起的執行緒,默認情況是最后進入的會先被喚起來,即LIFO的策略
notify執行之后立馬喚醒執行緒嗎
其實這個大家可以驗證一下,在notify之后寫一些邏輯,看這些邏輯是在其他執行緒被喚起之前還是之后執行,這個是個細節問題,可能大家并沒有關注到這個,其實hotspot里真正的實作是退出同步塊的時候才會去真正喚醒對應的執行緒,不過這個也是個默認策略,也可以改的,在notify之后立馬喚醒相關執行緒,
notifyAll是怎么實作全喚起的
或許大家立馬想到這個簡單,一個for回圈就搞定了,不過在jvm里沒實作這么簡單,而是借助了monitorexit,上面我提到了當某個執行緒從wait狀態恢復出來的時候,要先獲取鎖,然后再退出同步塊,所以notifyAll的實作是呼叫notify的執行緒在退出其同步塊的時候喚醒起最后一個進入wait狀態的執行緒,然后這個執行緒退出同步塊的時候繼續喚醒其倒數第二個進入wait狀態的執行緒,依次類推,同樣這這是一個策略的問題,jvm里提供了挨個直接喚醒執行緒的引數,不過都很罕見就不提了,
wait的執行緒是否會影響CPU的load負載么?
這個或許是大家比較關心的話題,因為關乎系統性能問題,wait/nofity底層是通過jvm里的park/unpark機制來實作的,在linux下這種機制又是通過pthread_cond_wait/pthread_cond_signal來玩的,因此當執行緒進入到wait狀態的時候其實是會放棄cpu的,也就是說這類執行緒是不會占用cpu資源,
小結
小結
今天這一節成長記, 你應該掌握如下知識:
1) synchronized鎖升級的整個詳細的程序
鎖的升級流程簡單來說是,無鎖->偏向鎖->自旋鎖->重量級鎖,除此也有很多其他升級的分支,你一定要記住如下這個圖就可以了,
2) synchronized不同鎖的核心原理
JVM基于Markword的鎖實作機制
偏向鎖中的JavaThread指標的作用
輕量級鎖(自旋鎖)中的LockRecord的作用
重量級鎖中的ObjectMonitor的作用
3) wait和notify的實作原理
4) synchronized鎖、wait和notify相關細節問題
本文由博客群發一文多發等運營工具平臺 OpenWrite 發布
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/331958.html
標籤:Java

