摘要:基本元素機(jī)制需要幾個(gè)元素來(lái)配合,分別是臨界區(qū)對(duì)象及鎖條件變量以及定義在對(duì)象上的,操作。這個(gè)外部條件在機(jī)制中稱為條件變量。提供的機(jī)制,其實(shí)是,等元素合作形成的,甚至說(shuō)外部的條件變量也是個(gè)組成部分。
monitor的概念
管程,英文是 Monitor,也常被翻譯為“監(jiān)視器”,monitor 不管是翻譯為“管程”還是“監(jiān)視器”,都是比較晦澀的,通過(guò)翻譯后的中文,并無(wú)法對(duì) monitor 達(dá)到一個(gè)直觀的描述。
在《操作系統(tǒng)同步原語(yǔ)》 這篇文章中,介紹了操作系統(tǒng)在面對(duì) 進(jìn)程/線程 間同步的時(shí)候,所支持的一些同步原語(yǔ),其中 semaphore 信號(hào)量 和 mutex 互斥量是最重要的同步原語(yǔ)。
在使用基本的 mutex 進(jìn)行并發(fā)控制時(shí),需要程序員非常小心地控制 mutex 的 down 和 up 操作,否則很容易引起死鎖等問(wèn)題。為了更容易地編寫出正確的并發(fā)程序,所以在 mutex 和 semaphore 的基礎(chǔ)上,提出了更高層次的同步原語(yǔ) monitor,不過(guò)需要注意的是,操作系統(tǒng)本身并不支持 monitor 機(jī)制,實(shí)際上,monitor 是屬于編程語(yǔ)言的范疇,當(dāng)你想要使用 monitor 時(shí),先了解一下語(yǔ)言本身是否支持 monitor 原語(yǔ),例如 C 語(yǔ)言它就不支持 monitor,Java 語(yǔ)言支持 monitor。
一般的 monitor 實(shí)現(xiàn)模式是編程語(yǔ)言在語(yǔ)法上提供語(yǔ)法糖,而如何實(shí)現(xiàn) monitor 機(jī)制,則屬于編譯器的工作,Java 就是這么干的。
monitor 的重要特點(diǎn)是,同一個(gè)時(shí)刻,只有一個(gè) 進(jìn)程/線程 能進(jìn)入 monitor 中定義的臨界區(qū),這使得 monitor 能夠達(dá)到互斥的效果。但僅僅有互斥的作用是不夠的,無(wú)法進(jìn)入 monitor 臨界區(qū)的 進(jìn)程/線程,它們應(yīng)該被阻塞,并且在必要的時(shí)候會(huì)被喚醒。顯然,monitor 作為一個(gè)同步工具,也應(yīng)該提供這樣的管理 進(jìn)程/線程 狀態(tài)的機(jī)制。想想我們?yōu)槭裁从X(jué)得 semaphore 和 mutex 在編程上容易出錯(cuò),因?yàn)槲覀冃枰ビH自操作變量以及對(duì) 進(jìn)程/線程 進(jìn)行阻塞和喚醒。monitor 這個(gè)機(jī)制之所以被稱為“更高級(jí)的原語(yǔ)”,那么它就不可避免地需要對(duì)外屏蔽掉這些機(jī)制,并且在內(nèi)部實(shí)現(xiàn)這些機(jī)制,使得使用 monitor 的人看到的是一個(gè)簡(jiǎn)潔易用的接口。
monitor 基本元素monitor 機(jī)制需要幾個(gè)元素來(lái)配合,分別是:
臨界區(qū)
monitor 對(duì)象及鎖
條件變量以及定義在 monitor 對(duì)象上的 wait,signal 操作。
使用 monitor 機(jī)制的目的主要是為了互斥進(jìn)入臨界區(qū),為了做到能夠阻塞無(wú)法進(jìn)入臨界區(qū)的 進(jìn)程/線程,還需要一個(gè) monitor object 來(lái)協(xié)助,這個(gè) monitor object 內(nèi)部會(huì)有相應(yīng)的數(shù)據(jù)結(jié)構(gòu),例如列表,來(lái)保存被阻塞的線程;同時(shí)由于 monitor 機(jī)制本質(zhì)上是基于 mutex 這種基本原語(yǔ)的,所以 monitor object 還必須維護(hù)一個(gè)基于 mutex 的鎖。
此外,為了在適當(dāng)?shù)臅r(shí)候能夠阻塞和喚醒 進(jìn)程/線程,還需要引入一個(gè)條件變量,這個(gè)條件變量用來(lái)決定什么時(shí)候是“適當(dāng)?shù)臅r(shí)候”,這個(gè)條件可以來(lái)自程序代碼的邏輯,也可以是在 monitor object 的內(nèi)部,總而言之,程序員對(duì)條件變量的定義有很大的自主性。不過(guò),由于 monitor object 內(nèi)部采用了數(shù)據(jù)結(jié)構(gòu)來(lái)保存被阻塞的隊(duì)列,因此它也必須對(duì)外提供兩個(gè) API 來(lái)讓線程進(jìn)入阻塞狀態(tài)以及之后被喚醒,分別是 wait 和 notify。
monitor 是操作系統(tǒng)提出來(lái)的一種高級(jí)原語(yǔ),但其具體的實(shí)現(xiàn)模式,不同的編程語(yǔ)言都有可能不一樣。以下以 Java 的 monitor 為例子,來(lái)講解 monitor 在 Java 中的實(shí)現(xiàn)方式。
臨界區(qū)的圈定在 Java 中,可以采用 synchronized 關(guān)鍵字來(lái)修飾實(shí)例方法、類方法以及代碼塊,如下所示:
/** * @author beanlam * @version 1.0 * @date 2018/9/12 */ public class Monitor { private Object ANOTHER_LOCK = new Object(); private synchronized void fun1() { } public static synchronized void fun2() { } public void fun3() { synchronized (this) { } } public void fun4() { synchronized (ANOTHER_LOCK) { } } }
被 synchronized 關(guān)鍵字修飾的方法、代碼塊,就是 monitor 機(jī)制的臨界區(qū)。
monitor object可以發(fā)現(xiàn),上述的 synchronized 關(guān)鍵字在使用的時(shí)候,往往需要指定一個(gè)對(duì)象與之關(guān)聯(lián),例如 synchronized(this),或者 synchronized(ANOTHER_LOCK),synchronized 如果修飾的是實(shí)例方法,那么其關(guān)聯(lián)的對(duì)象實(shí)際上是 this,如果修飾的是類方法,那么其關(guān)聯(lián)的對(duì)象是 this.class。總之,synchronzied 需要關(guān)聯(lián)一個(gè)對(duì)象,而這個(gè)對(duì)象就是 monitor object。
monitor 的機(jī)制中,monitor object 充當(dāng)著維護(hù) mutex以及定義 wait/signal API 來(lái)管理線程的阻塞和喚醒的角色。
Java 語(yǔ)言中的 java.lang.Object 類,便是滿足這個(gè)要求的對(duì)象,任何一個(gè) Java 對(duì)象都可以作為 monitor 機(jī)制的 monitor object。
Java 對(duì)象存儲(chǔ)在內(nèi)存中,分別分為三個(gè)部分,即對(duì)象頭、實(shí)例數(shù)據(jù)和對(duì)齊填充,而在其對(duì)象頭中,保存了鎖標(biāo)識(shí);同時(shí),java.lang.Object 類定義了 wait(),notify(),notifyAll() 方法,這些方法的具體實(shí)現(xiàn),依賴于一個(gè)叫 ObjectMonitor 模式的實(shí)現(xiàn),這是 JVM 內(nèi)部基于 C++ 實(shí)現(xiàn)的一套機(jī)制,基本原理如下所示:
當(dāng)一個(gè)線程需要獲取 Object 的鎖時(shí),會(huì)被放入 EntrySet 中進(jìn)行等待,如果該線程獲取到了鎖,成為當(dāng)前鎖的 owner。如果根據(jù)程序邏輯,一個(gè)已經(jīng)獲得了鎖的線程缺少某些外部條件,而無(wú)法繼續(xù)進(jìn)行下去(例如生產(chǎn)者發(fā)現(xiàn)隊(duì)列已滿或者消費(fèi)者發(fā)現(xiàn)隊(duì)列為空),那么該線程可以通過(guò)調(diào)用 wait 方法將鎖釋放,進(jìn)入 wait set 中阻塞進(jìn)行等待,其它線程在這個(gè)時(shí)候有機(jī)會(huì)獲得鎖,去干其它的事情,從而使得之前不成立的外部條件成立,這樣先前被阻塞的線程就可以重新進(jìn)入 EntrySet 去競(jìng)爭(zhēng)鎖。這個(gè)外部條件在 monitor 機(jī)制中稱為條件變量。
synchronized 關(guān)鍵字synchronized 關(guān)鍵字是 Java 在語(yǔ)法層面上,用來(lái)讓開(kāi)發(fā)者方便地進(jìn)行多線程同步的重要工具。要進(jìn)入一個(gè) synchronized 方法修飾的方法或者代碼塊,會(huì)先獲取與 synchronized 關(guān)鍵字綁定在一起的 Object 的對(duì)象鎖,這個(gè)鎖也限定了其它線程無(wú)法進(jìn)入與這個(gè)鎖相關(guān)的其它 synchronized 代碼區(qū)域。
網(wǎng)上很多文章以及資料,在分析 synchronized 的原理時(shí),基本上都會(huì)說(shuō) synchronized 是基于 monitor 機(jī)制實(shí)現(xiàn)的,但很少有文章說(shuō)清楚,都是模糊帶過(guò)。
參照前面提到的 Monitor 的幾個(gè)基本元素,如果 synchronized 是基于 monitor 機(jī)制實(shí)現(xiàn)的,那么對(duì)應(yīng)的元素分別是什么?
它必須要有臨界區(qū),這里的臨界區(qū)我們可以認(rèn)為是對(duì)對(duì)象頭 mutex 的 P 或者 V 操作,這是個(gè)臨界區(qū)
那 monitor object 對(duì)應(yīng)哪個(gè)呢?mutex?總之無(wú)法找到真正的 monitor object。
所以我認(rèn)為“synchronized 是基于 monitor 機(jī)制實(shí)現(xiàn)的”這樣的說(shuō)法是不正確的,是模棱兩可的。
Java 提供的 monitor 機(jī)制,其實(shí)是 Object,synchronized 等元素合作形成的,甚至說(shuō)外部的條件變量也是個(gè)組成部分。JVM 底層的 ObjectMonitor 只是用來(lái)輔助實(shí)現(xiàn) monitor 機(jī)制的一種常用模式,但大多數(shù)文章把 ObjectMonitor 直接當(dāng)成了 monitor 機(jī)制。
我覺(jué)得應(yīng)該這么理解:Java 對(duì) monitor 的支持,是以機(jī)制的粒度提供給開(kāi)發(fā)者使用的,也就是說(shuō),開(kāi)發(fā)者要結(jié)合使用 synchronized 關(guān)鍵字,以及 Object 的 wait / notify 等元素,才能說(shuō)自己利用 monitor 的機(jī)制去解決了一個(gè)生產(chǎn)者消費(fèi)者的問(wèn)題。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/77138.html
摘要:就像是擁有一個(gè)特殊房間的建筑,在同一時(shí)間里,這間特殊的房間只能被一個(gè)線程擁有。當(dāng)線程獲得這就是一個(gè)信號(hào),線程開(kāi)始擁有的所有權(quán),能夠繼續(xù)執(zhí)行。注意到的以及要喚醒的對(duì)象就處于換句話說(shuō),如果退出的線程沒(méi)有執(zhí)行那么只有能夠獲取執(zhí)行的權(quán)限。 Java支持同步機(jī)制的是Monitor。Monitor就像是擁有一個(gè)特殊房間的建筑,在同一時(shí)間里,這間特殊的房間只能被一個(gè)線程擁有。 enter the ...
摘要:如果同一個(gè)線程再次請(qǐng)求該鎖,計(jì)數(shù)器會(huì)遞增,每次占有的線程退出同步代碼塊時(shí)計(jì)數(shù)器會(huì)遞減,直至減為時(shí)鎖才會(huì)被釋放。表示或在該上的所有線程的個(gè)數(shù)用來(lái)實(shí)現(xiàn)重入鎖的計(jì)數(shù)。只有兩種可能的值表示沒(méi)有需要喚醒的線程表示要喚醒一個(gè)繼任線程來(lái)競(jìng)爭(zhēng)鎖。 一、synchronized 1.類型 (1)對(duì)象鎖 對(duì)象鎖是作用在實(shí)例方法或者一個(gè)對(duì)象實(shí)例上面的 一個(gè)類可以有多個(gè)實(shí)例對(duì)象,因此一個(gè)類的對(duì)象鎖可能會(huì)有多個(gè)...
摘要:的關(guān)鍵字中的塊使用關(guān)鍵字進(jìn)行標(biāo)記。由于每個(gè)類只有一個(gè)類對(duì)象存在于中,因此全局同時(shí)只有一個(gè)線程能夠進(jìn)入到同一個(gè)類的靜態(tài)同步方法中。同步代碼塊使這種期望成為可能。注意同步代碼塊如何在括號(hào)中接受一個(gè)對(duì)象。相同的實(shí)例被傳入兩個(gè)不同的線程實(shí)例中。 Java的synchronized塊標(biāo)記一個(gè)方法或一個(gè)代碼塊為同步的。synchronized塊能用于防止出現(xiàn)競(jìng)態(tài)條件。 Java的synchroni...
摘要:每一個(gè)被鎖住的對(duì)象都會(huì)和一個(gè)關(guān)聯(lián)對(duì)象頭的中的指向的起始地址,同時(shí)中有一個(gè)字段存放擁有該鎖的線程的唯一標(biāo)識(shí),表示該鎖被這個(gè)線程占用。 jdk 6 對(duì)鎖進(jìn)行了優(yōu)化,讓他看起來(lái)不再那么笨重,synchronized有三種形式:偏向鎖,輕量級(jí)鎖,重量級(jí)鎖. 介紹三種鎖之前,引入幾個(gè)接下來(lái)會(huì)出現(xiàn)的概念 mark work: 對(duì)象頭,對(duì)象頭中存儲(chǔ)了一些對(duì)象的信息,這個(gè)是鎖的根本,任何鎖都需要依賴m...
摘要:等待一段時(shí)間是否有線程喚醒鎖,如果沒(méi)有,超時(shí)自動(dòng)喚醒。隨機(jī)喚醒等待隊(duì)列中的等待同一個(gè)鎖的一個(gè)線程,使這個(gè)線程退出等待隊(duì)列,進(jìn)入可運(yùn)行狀態(tài)。條件隊(duì)列中是處于等待狀態(tài)的線程,等待特定條件為真。在一般情況下,總應(yīng)該調(diào)用喚醒所有需要被喚醒的線程。 方法 java.lang.Object public final native void wait() throws InterruptedExce...
閱讀 1681·2021-09-22 15:21
閱讀 2897·2021-09-09 09:32
閱讀 2746·2021-09-02 09:52
閱讀 3336·2019-08-30 14:02
閱讀 2252·2019-08-26 13:25
閱讀 1496·2019-08-26 13:24
閱讀 1638·2019-08-26 10:31
閱讀 1593·2019-08-26 10:16