操做系統使用信號量解決併發問題,Java選擇使用管程(Monitor)解決併發問題。信號量和管程是等價的,可使用信號量實現管程,也可使用管程實現信號量。java
管程就是指管理共享變量,以及對共享變量的相關操做。具體到 Java 語言中,管程就是管理類的成員變量和方法,讓這個類是線程安全的。管程的發展史中,前後出現過三種管程模型,Hasen 模型、Hoare 模型和 MESA 模型,Java 使用的是 MESA 模型。編程
咱們用管程模型主要是解決併發編程中的兩個核心問題,互斥和同步。互斥是指同一時刻只容許一個線程訪問共享資源,同步則是指線程之間如何通訊、寫做。安全
那麼,Java 所採用的 MESA 模型是如何解決互斥和同步問題的呢?併發
管程模型解決互斥問題的方法是:將共享變量及對共享變量的操做統一封裝起來。spa
以下圖所示,管程 X 將共享變量 queue,及其入隊出隊操做 enq() 和 dep() 封裝起來。線程 A 和線程 B 想要訪問共享變量 queue,就須要經過 enq() 和 deq() 來實現,而 enq() 和 deq() 保證互斥,只容許一個線程進入管程。操作系統
MESA 模型解決同步問題能夠類比去醫院就醫。患者首先須要排隊等待醫生叫好,醫生診斷被叫到號的患者。期間,患者若是須要進行其餘輔助的檢查,好比說排個 X 光,就須要去等待拍 X 光的醫生叫好。患者拍完 X 光以後,再次回到上一個醫生那裏,等待醫生再次診斷。線程
管程模型與看醫生的流程相似,管程入口處有一個等待隊列。當多個線程試圖進入管程內部的時候,只容許一個線程進入,其餘線程在等待隊列中等待。就和看醫生的時候排隊同樣。3d
管程中還有一個條件變量的概念,每一個條件變量對應一個條件變量等待隊列。好比說有一個條件變量 A,當執行線程 T1 時發現不知足條件變量 A,T1 就會進入條件變量 A 的等待隊列中。就像去看醫生,醫生讓你先去排個 X 光,就要去拍 X 光的地方排隊。code
當執行線程 T2 時發現知足條件變量 A,就會喚醒條件變量 A 等待中的線程 T1,線程 T1 就會再次進入到入口等待隊列。就像拍完 X 光的人,再去看醫生。blog
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 參考了 MESA 模型,語言內置的管程(synchronized)對 MESA 模型進行了精簡。MESA 模型中,條件變量能夠有多個,Java 語言內置的管程裏只有一個條件變量。
Java SDK 併發包實現的管程支持多個條件變量,不過併發包裏的鎖,須要開發人員本身進行加鎖和解鎖操做。