JavaStorm 關注公衆號獲取更多併發
在吃透 Syncchronized 原理 中介紹了關於 Synchronize
的實現原理,無論是同步方法還是同步代碼塊,無論是ACC_SYNCHRONIZED
還是monitorenter
、monitorexit
都是基於Monitor
實現的,那麼這篇來介紹下什麼是Monitor。
所謂管程:指的是管理共享變量以及對共享變量的操作過程,讓它們支持併發。翻譯爲 Java 就是管理類的成員變量和成員方法,讓這個類是線程安全的。
是一種程序結構,結構內的多個子程序(對象或模塊)形成的多個工作線程互斥訪問共享資源。這些共享資源一般是硬件設備或一羣變量。管程實現了在一個時間點,最多隻有一個線程在執行管程的某個子程序。與那些通過修改數據結構實現互斥訪問的併發程序設計相比,管程實現很大程度上簡化了程序設計。 管程提供了一種機制,線程可以臨時放棄互斥訪問,等待某些條件得到滿足後,重新獲得執行權恢復它的互斥訪問。
MESA 模型
在管程的發展史上,先後出現過三種不同的管程模型,分別是:Hasen
模型、Hoare
模型和 MESA
模型。其中,現在廣泛應用的是 MESA
模型,並且 Java
管程的實現參考的也是 MESA
模型。所以今天我們重點介紹一下MESA
模型。
在併發領域,有兩個核心問題:一個是互斥,一個是同步。管程就是來解決這兩個問題的。
- 互斥:同一時刻只允許一個線程訪問共享資源。
- 同步:線程之間如何通信、協作。
管程互斥與同步實現
它的思路很簡單,將共享變量以及對共享變量的操作統一封裝起來。如下圖所示,管程 A 將共享變量 data 和相關的操作入隊enq()
、出隊deq()
封裝起來。線程 A 和線程 B想訪問共享變量 data ,只能通過調用管程提供的 enq()
和 deq()
。當然前提是 enq()
、deq()
保證互斥性,只允許一個線程進入管程。是不是很有面向對象的感覺。
()
在管程模型裏,共享變量和對共享變量的操作是被封裝起來的,圖中最外層的框就代表封裝的意思。框的上面只有一個入口,並且在入口旁邊還有一個入口等待隊列。當多個線程同時試圖進入管程內部時,只允許一個線程進入,其他線程則在入口等待隊列中等待。這個過程類似就醫流程的分診,只允許一個患者就診,其他患者都在門口等待。
管程裏還引入了條件變量的概念,而且每個條件變量都對應有一個等待隊列,如下圖,條件變量 A 和條件變量 B 分別都有自己的等待隊列。
通過條件通知去喚醒等待隊列的線程競爭 鎖資源。
我們通過一段代碼說明,實現一個阻塞隊列,隊列分別有出隊與入隊,都是要先獲取互斥鎖,就像管程中的入口。
- 對於入隊操作,如果隊列已滿,就需要等待直到隊列不滿,所以這裏用了
notFull.await();
。 - 對於出隊操作,如果隊列爲空,就需要等待直到隊列不空,所以就用了
notEmpty.await();
。 - 如果入隊成功,那麼隊列就不空了,就需要通知條件變量:隊列不空
notEmpty
對應的等待隊列。 - 如果出隊成功,那就隊列就不滿了,就需要通知條件變量:隊列不滿
notFull
對應的等待隊列。
public class BlockedQueue<T>{
final Lock lock = new ReentrantLock();
// 條件變量:隊列不滿
final Condition notFull = lock.newCondition();
// 條件變量:隊列不空
final Condition notEmpty = lock.newCondition();
// 入隊
void enq(T x) {
lock.lock();
try {
while (隊列已滿){
// 等待隊列不滿
notFull.await();
}
// 省略入隊操作...
// 入隊後, 通知可出隊
notEmpty.signal();
}finally {
lock.unlock();
}
}
// 出隊
void deq(){
lock.lock();
try {
while (隊列已空){
// 等待隊列不空
notEmpty.await();
}
// 省略出隊操作...
// 出隊後,通知可入隊
notFull.signal();
}finally {
lock.unlock();
}
}
}
在這段示例代碼中,我們用了 Java 併發包裏面的 Lock 和 Condition,如果你看着吃力,也沒關係,後面我們還會詳細介紹,這個例子只是先讓你明白條件變量及其等待隊列是怎麼回事。需要注意的是:await() 和前面我們提到的 wait() 語義是一樣的;signal() 和前面我們提到的 notify() 語義是一樣的。管程通過條件隊列通信實現了同步,爲我們 Java中的併發編程提供了基本支持。
關注公衆號 JavaStorm 獲取更多併發原理