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