管程:併發編程的基石

文章目錄
1. 定義
2. MESA模型
2.1 互斥
2.2 同步
3. synchronized
4. Lock與Condition
5. 總結
本文主要參考了極客時間上王寶令老師的《Java併發編程實戰》,裏面大部分內容來自這門課程,本文中的幾張圖也是參考課程裏面的圖畫的。個人覺得這門課程比較基礎,相對而言比較簡單,老師講解得也十分清晰,適合併發編程的入門,有需要的朋友可以點擊文末的二維碼連接去了解。

1. 定義
Monitor在英語中直譯是監視器的意思,但是在操作系統中通常被翻譯爲管程,是用來實現併發的一種技術,它解決了併發編程中的兩大核心問題:互斥與同步。所以管程的定義是:用來管理共享變量以及對共享變量操作的過程。
歷史上出現過三種管程模型,MESA模型、Hasen模型、Hoare模型。而在Java中,管程的實現是根據MESA模型實現的。
2. MESA模型
管程實現了併發編程領域的兩大核心問題:互斥與同步,那麼MESA模型是如何來實現互斥與同步的呢?

2.1 互斥
管程通過對共享變量以及對操作共享變量方法的進行了封裝,在同一時刻,只有一個線程進入到管程中,這樣就保證了只有一個線程來操作共享變量,實現了互斥的功能。

如上圖中,管程X對共享變量count,以及對操作共享變量count的兩個方法遞增:increment()和遞減:decrement()進行了封裝,要想訪問共享變量count,只能通過increment()和decrement()。由於管程保證了同時只允許一個線程進入,所以保證了increment()和decrement()的互斥性。
2.2 同步
如下圖的管程模型圖中,方框代表管程對共享變量以及操作共享變量方法的封裝,在入口處有一個等待隊列,當有多個線程試圖進入管程時,管程只允許一個線程進入,其他的線程進入到等待隊列中。
在管程中引入了條件變量的概念,每個條件變量都對應一個等待隊列。如圖中的條件變量A和B分別對應一個等待隊列。

條件變量和等待隊列的作用就是爲了實現線程之間的同步問題。可以結合下面的例子理解。
假設有如下場景,對於共享變量count,初始值爲0,我們需要對它進行遞增或者遞減操作,但是在進行操作時,需要滿足如下條件,進行遞增時,count的值不能大於等於10,進行遞減時,count的值需要大於0。
假設線程T1要對共享變量進行遞減操作,由於此時count值爲0,所以不能進行遞減操作,這個時候就應該讓線程T1進行等待。去哪兒等待呢?去條件變量對應的等待隊列裏面進行等待。所以此時需要調用count>0這個條件變量對象的wait()方法,這樣線程T1就進入到count>0這個條件變量的等待隊列中等待。
再假設線程T2要對共享變量進行遞增操作,由於此時count=0,滿足count值不能大於等於10這個條件,所以T2執行遞增操作,操作之後count變爲1,那麼此時對於條件變量count>0對於線程T1來說已經成立了,所以這個時候T2需要通知T1條件滿足了。那麼如何通知呢?通過調用count>0這個條件變量對象的notify()或者notifyAll()方法。通知完成後,T1線程會從條件變量的等待隊列中出來,但是此時T1不會立馬執行,而是需要重新進入到管程入口處的等待隊列中。
可以結合如下代碼理解。在代碼中使用了java.util.concurrent包下的類Lock和Condition,await()方法和wait()方法作用是一樣的,signalAll()和notifyAll()作用是一樣的。
public class Count {

    volatile int count = 0;

    final Lock lock = new ReentrantLock();

    // 小於10條件變量
    final Condition lessThanTen = lock.newCondition();

    // 大於0條件變量
    final Condition moreThanZero = lock.newCondition();

    void increment(){
        lock.lock();
        try{
            while(!count小於10){
                // 小於10這個條件變量不滿足,調用await()方法進入到條件變量的等待隊列中
                lessThanTen.await();
            }
            // 執行遞增操作
            // 遞增完成以後,通知大於0這個條件等待隊列中的線程
            moreThanZero.signalAll();
        }finally {
          lock.unlock();
        }
    }

    void decrement(){
        lock.lock();
        try{
            while(!count大於0){
                // 大於0這個條件變量不滿足,調用await()方法進入到條件變量的等待隊列中
                moreThanZero.await();
            }
            // 執行遞減操作

            // 執行遞減操作以後,通知小於10這個條件等待隊列中的線程
            lessThanTen.signalAll();
        }finally{
            lock.unlock();
        }
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
通過對上面MESA管程模型的介紹,到這裏相信你對管程有了一定的認識。那管程在Java中到底是如何實現的呢?
3. synchronized
Java語言中爲我們提供了synchronized關鍵字來實現鎖。synchronized關鍵詞實現的鎖是隱式鎖,爲什麼稱之爲隱式鎖呢?是因爲在編譯器編譯Java文件時,當碰到synchronized時,會自動加上加鎖指令和解鎖指令,即monitorenter和monitorexit,這一步對於開發人員來說是透明的,所以說它是隱式鎖。它的底層實現就是通過管程實現的,前面我們介紹的管程模型中,可以支持多個條件變量,但是synchronized的管程實現只支持一個條件變量。它的管程示意圖如下:

那麼在虛擬機中,是如何實現管程的呢?那就是ObjectMonitor這個對象了。在openJDK的源碼中,有這樣兩個源文件:objectMonitor.cpp和objectMonitor.hpp。前者的文件中實現了鎖的獲取、釋放等方法,後者定義了前者需要的頭文件。在objectMonitor.hpp文件中定義了這樣一個結構:
ObjectMonitor() {
    _header       = NULL;
    _count        = 0;
    _waiters      = 0,
    _recursions   = 0;
    _object       = NULL;
    _owner        = NULL;
    // 條件等待隊列
    _WaitSet      = NULL;
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;
    FreeNext      = NULL ;
    // 入口等待隊列
    _EntryList    = NULL ;
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
    _previous_owner_tid = 0;
 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
在ObjectMonitor這個結構中,有兩個重要的屬性:_WaitSet和_EntryList,這兩個屬性分別對應管程中條件等待隊列和入口等待隊列。
4. Lock與Condition
Lock和Condition是java.util.concurrent包下的類,與synchronized關鍵字實現鎖的原理不一樣,Lock是在Java層面實現的,而synchronized是通過JVM虛擬機實現的鎖。

Lock和Condition也是通過管程模型來實現鎖的。其中Lock是是用來實現互斥的,Condition是用來實現同步的。
與synchronized不同的是,Lock與Condition實現的管程,支持多條件變量,而synchronized的管程實現只支持單個條件變量。
5. 總結
本文主要介紹了管程的模型以及管程是如何來實現併發的,
本文結合Java中synchronized和Lock介紹了管程的實現。
本文的內容更偏於個人的學習筆記,很多地方講解得不是很詳細,如果想了解更加詳細的,可以去看看極客時間上《Java併發編程實戰》這門課程,下方是課程的二維碼鏈接。
————————————————
版權聲明:本文爲CSDN博主「天堂2013」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_34436819/article/details/101913269

 

 

 

深層次探討mutex與semaphore之間的區別(上)

 

semaphore和mutex的區別?

 https://www.zhihu.com/question/47704079

 

java的同步底層就是使用mutex來實現的管程,最後一級重量級鎖 使用的是object monitor

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章