自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

面試突擊:Synchronized底層是如何實(shí)現(xiàn)的?

開發(fā) 后端
synchronized 同步鎖是通過(guò) JVM 內(nèi)置的 Monitor 監(jiān)視器實(shí)現(xiàn)的,而監(jiān)視器又是依賴操作系統(tǒng)的互斥鎖 Mutex 實(shí)現(xiàn)的。

作者 | 磊哥

來(lái)源 | Java面試真題解析(ID:aimianshi666)

轉(zhuǎn)載請(qǐng)聯(lián)系授權(quán)(微信ID:GG_Stone)

想了解 synchronized 是如何運(yùn)行的?就要先搞清楚 synchronized 是如何實(shí)現(xiàn)?synchronized 同步鎖是通過(guò) JVM 內(nèi)置的 Monitor 監(jiān)視器實(shí)現(xiàn)的,而監(jiān)視器又是依賴操作系統(tǒng)的互斥鎖 Mutex 實(shí)現(xiàn)的,那接下來(lái)我們先來(lái)了解一下監(jiān)視器。

監(jiān)視器

監(jiān)視器是一個(gè)概念或者說(shuō)是一個(gè)機(jī)制,它用來(lái)保障在任何時(shí)候,只有一個(gè)線程能夠執(zhí)行指定區(qū)域的代碼。

一個(gè)監(jiān)視器像是一個(gè)建筑,建筑里有一個(gè)特殊的房間,這個(gè)房間同一時(shí)刻只能被一個(gè)線程所占有。一個(gè)線程從進(jìn)入該房間到離開該房間,可以全程獨(dú)占該房間的所有數(shù)據(jù)。進(jìn)入該建筑叫做進(jìn)入監(jiān)視器(entering the monitor),進(jìn)入該房間叫做獲得監(jiān)視器(acquiring the monitor),獨(dú)自占有該房間叫做擁有監(jiān)視器(owning the monitor),離開該房間叫做釋放監(jiān)視器(releasing the monitor),離開該建筑叫做退出監(jiān)視器(exiting the monitor)。

嚴(yán)格意義來(lái)說(shuō)監(jiān)視器和鎖的概念是不同的,但很多地方也把二者相互指代。

底層實(shí)現(xiàn)

下面我們?cè)诖a中添加一個(gè) synchronized 代碼塊,來(lái)觀察一下它在字節(jié)碼層面是如何實(shí)現(xiàn)的?示例代碼如下:

public class SynchronizedToMonitorExample {
public static void main(String[] args) {
int count = 0;
synchronized (SynchronizedToMonitorExample.class) {
for (int i = 0; i < 10; i++) {
count++;
}
}
System.out.println(count);
}
}

當(dāng)我們將上述代碼編譯成字節(jié)碼之后,得到的結(jié)果是這樣的: 從上述結(jié)果我們可以看出,在 main 方法中多了一對(duì) monitorenter 和 monitorexit 的指令,它們的含義是:

  • monitorenter:表示進(jìn)入監(jiān)視器。
  • monitorexit:表示退出監(jiān)視器。

由此可知 synchronized 是依賴 Monitor 監(jiān)視器實(shí)現(xiàn)的。

執(zhí)行流程

在 Java 中,synchronized 是非公平鎖,也是可以重入鎖。所謂的非公平鎖是指,線程獲取鎖的順序不是按照訪問(wèn)的順序先來(lái)先到的,而是由線程自己競(jìng)爭(zhēng),隨機(jī)獲取到鎖??芍厝腈i指的是,一個(gè)線程獲取到鎖之后,可以重復(fù)得到該鎖。這些內(nèi)容是理解接下來(lái)內(nèi)容的前置知識(shí)。在 HotSpot 虛擬機(jī)中,Monitor 底層是由 C++實(shí)現(xiàn)的,它的實(shí)現(xiàn)對(duì)象是 ObjectMonitor,ObjectMonitor 結(jié)構(gòu)體的實(shí)現(xiàn)如下:

ObjectMonitor::ObjectMonitor() {  
_header = NULL;
_count = 0;
_waiters = 0,
_recursions = 0; //線程的重入次數(shù)
_object = NULL;
_owner = NULL; //標(biāo)識(shí)擁有該monitor的線程
_WaitSet = NULL; //等待線程組成的雙向循環(huán)鏈表,_WaitSet是第一個(gè)節(jié)點(diǎn)
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ; //多線程競(jìng)爭(zhēng)鎖進(jìn)入時(shí)的單向鏈表
FreeNext = NULL ;
_EntryList = NULL ; //_owner從該雙向循環(huán)鏈表中喚醒線程結(jié)點(diǎn),_EntryList是第一個(gè)節(jié)點(diǎn)
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
}

在以上代碼中有幾個(gè)關(guān)鍵的屬性:

  • _count:記錄該線程獲取鎖的次數(shù)(也就是前前后后,這個(gè)線程一共獲取此鎖多少次)。
  • _recursions:鎖的重入次數(shù)。
  • _owner:The Owner 擁有者,是持有該 ObjectMonitor(監(jiān)視器)對(duì)象的線程;
  • _EntryList:EntryList 監(jiān)控集合,存放的是處于阻塞狀態(tài)的線程隊(duì)列,在多線程下,競(jìng)爭(zhēng)失敗的線程會(huì)進(jìn)入 EntryList 隊(duì)列。
  • _WaitSet:WaitSet 待授權(quán)集合,存放的是處于 wait 狀態(tài)的線程隊(duì)列,當(dāng)線程執(zhí)行了 wait() 方法之后,會(huì)進(jìn)入 WaitSet 隊(duì)列。

監(jiān)視器執(zhí)行的流程如下:

  1. 線程通過(guò) CAS(對(duì)比并替換)嘗試獲取鎖,如果獲取成功,就將 _owner 字段設(shè)置為當(dāng)前線程,說(shuō)明當(dāng)前線程已經(jīng)持有鎖,并將 _recursions 重入次數(shù)的屬性 +1。如果獲取失敗則先通過(guò)自旋 CAS 嘗試獲取鎖,如果還是失敗則將當(dāng)前線程放入到 EntryList 監(jiān)控隊(duì)列(阻塞)。
  2. 當(dāng)擁有鎖的線程執(zhí)行了 wait 方法之后,線程釋放鎖,將 owner 變量恢復(fù)為 null 狀態(tài),同時(shí)將該線程放入 WaitSet 待授權(quán)隊(duì)列中等待被喚醒。
  3. 當(dāng)調(diào)用 notify 方法時(shí),隨機(jī)喚醒 WaitSet 隊(duì)列中的某一個(gè)線程,當(dāng)調(diào)用 notifyAll 時(shí)喚醒所有的 WaitSet 中的線程嘗試獲取鎖。
  4. 線程執(zhí)行完釋放了鎖之后,會(huì)喚醒 EntryList 中的所有線程嘗試獲取鎖。

以上就是監(jiān)視器的執(zhí)行流程,執(zhí)行流程如下圖所示:

總結(jié)

synchronized 同步鎖是通過(guò) JVM 內(nèi)置的 Monitor 監(jiān)視器實(shí)現(xiàn)的,而監(jiān)視器又是依賴操作系統(tǒng)的互斥鎖 Mutex 實(shí)現(xiàn)的。JVM 監(jiān)視器的執(zhí)行流程是:線程先通過(guò)自旋 CAS 的方式嘗試獲取鎖,如果獲取失敗就進(jìn)入 EntrySet 集合,如果獲取成功就擁有該鎖。當(dāng)調(diào)用 wait() 方法時(shí),線程釋放鎖并進(jìn)入 WaitSet 集合,等其他線程調(diào)用 notify 或 notifyAll 方法時(shí)再嘗試獲取鎖。鎖使用完之后就會(huì)通知 EntrySet 集合中的線程,讓它們嘗試獲取鎖。

參考資料

  • www.cnblogs.com/freelancy/p/15625602.html blog.csdn.net/qq_43783527/article/details/114669174
  • www.cnblogs.com/hongdada/p/14513036.html。
責(zé)任編輯:姜華 來(lái)源: Java面試真題解析
相關(guān)推薦

2022-04-11 07:40:45

synchroniz靜態(tài)方法程序

2021-01-08 08:34:09

Synchronize線程開發(fā)技術(shù)

2022-04-24 07:59:53

synchronizJVMAPI

2022-12-26 09:27:48

Java底層monitor

2022-01-13 06:59:40

HashMap底層面試

2022-04-06 07:50:28

線程安全代碼

2022-09-07 07:05:25

跨域問(wèn)題安全架構(gòu)

2022-04-20 07:47:00

notify喚醒線程JVM

2022-03-23 08:51:21

線程池Java面試題

2022-01-24 07:01:20

安全多線程版本

2022-03-14 07:32:06

線程池拒絕策略自定義

2022-02-28 07:01:22

線程中斷interrupt

2023-10-12 08:19:04

Monitor線程

2022-07-06 07:35:19

group byMySQL

2021-07-04 08:01:30

Synchronize線程安全并發(fā)編程

2024-03-15 15:12:27

關(guān)鍵字底層代碼

2022-08-01 07:07:15

粘包半包封裝

2022-05-30 07:34:33

三范式Java

2025-04-08 00:00:00

@AsyncSpring異步

2022-09-12 22:27:05

編程式事務(wù)聲明式事務(wù)對(duì)象
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)