管程(Moniter) 併發編程的基本心法

關注公衆號 JavaStorm 解鎖更多併發java

吃透 Syncchronized 原理 中介紹了關於 Synchronize的實現原理,不管是同步方法仍是同步代碼塊,不管是ACC_SYNCHRONIZED仍是monitorentermonitorexit都是基於Monitor實現的,那麼這篇來介紹下什麼是Monitor編程

所謂管程:指的是管理共享變量以及對共享變量的操做過程,讓它們支持併發。翻譯爲 Java 就是管理類的成員變量和成員方法,讓這個類是線程安全的。安全

是一種程序結構,結構內的多個子程序(對象或模塊)造成的多個工做線程互斥訪問共享資源。這些共享資源通常是硬件設備或一羣變量。管程實現了在一個時間點,最多隻有一個線程在執行管程的某個子程序。與那些經過修改數據結構實現互斥訪問的併發程序設計相比,管程實現很大程度上簡化了程序設計。 管程提供了一種機制,線程能夠臨時放棄互斥訪問,等待某些條件獲得知足後,從新得到執行權恢復它的互斥訪問。數據結構

MESA 模型

在管程的發展史上,前後出現過三種不一樣的管程模型,分別是:Hasen 模型、Hoare 模型和 MESA模型。其中,如今普遍應用的是 MESA 模型,而且 Java 管程的實現參考的也是 MESA 模型。因此今天咱們重點介紹一下MESA 模型。併發

在併發領域,有兩個核心問題:一個是互斥,一個是同步。管程就是來解決這兩個問題的。ui

  • 互斥:同一時刻只容許一個線程訪問共享資源。
  • 同步:線程之間如何通訊、協做。

管程互斥與同步實現

它的思路很簡單,將共享變量以及對共享變量的操做統一封裝起來。以下圖所示,管程 A 將共享變量 data 和相關的操做入隊enq()、出隊deq() 封裝起來。線程 A 和線程 B想訪問共享變量 data ,只能經過調用管程提供的 enq()deq() 。固然前提是 enq()deq() 保證互斥性,只容許一個線程進入管程。是否是頗有面向對象的感受。spa

管程

在管程模型裏,共享變量和對共享變量的操做是被封裝起來的,圖中最外層的框就表明封裝的意思。框的上面只有一個入口,而且在入口旁邊還有一個入口等待隊列。當多個線程同時試圖進入管程內部時,只容許一個線程進入,其餘線程則在入口等待隊列中等待。這個過程相似就醫流程的分診,只容許一個患者就診,其餘患者都在門口等待。線程

管程裏還引入了條件變量的概念,並且每一個條件變量都對應有一個等待隊列,以下圖,條件變量 A 和條件變量 B 分別都有本身的等待隊列。翻譯

經過條件通知去喚醒等待隊列的線程競爭 鎖資源。設計

咱們經過一段代碼說明,實現一個阻塞隊列,隊列分別有出隊與入隊,都是要先獲取互斥鎖,就像管程中的入口。

  1. 對於入隊操做,若是隊列已滿,就須要等待直到隊列不滿,因此這裏用了notFull.await();
  2. 對於出隊操做,若是隊列爲空,就須要等待直到隊列不空,因此就用了notEmpty.await();
  3. 若是入隊成功,那麼隊列就不空了,就須要通知條件變量:隊列不空notEmpty對應的等待隊列。
  4. 若是出隊成功,那就隊列就不滿了,就須要通知條件變量:隊列不滿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 獲取更多併發原理

相關文章
相關標籤/搜索