Java併發-管程

爲什麼需要了解管程

Java併發編程是Java中高級程序員必備的一項技能,但是真正學明白併發編程也並非易事。正如Java併發編程實踐中的一句話“編寫正確的程序並不容易,而編寫正確的併發程序就更難了”,Java裏併發的知識很瑣碎,看懂難,會用更難。然後,我們常常一頭扎進了Java併發編程技術的細節,卻發現剪不斷理還亂,終不得要領。這是因爲我們忽略了技術背後的理論和模型,而理論和模型往往比具體的技術更爲重要。那Java併發裏面的理論和模型是什麼呢?那便要從操作系統中解決併發問題的一種模型管程講起了。

什麼是管程

管程是將共享變量及對共享變量的操作封裝起來的管理程序。操作系統中的描述爲:代表共享資源的數據結構,以及由對該共享數據結構實施操作的一組過程所組成的資源管理程序,共同構成了一個操作系統的資源管理模塊,我們稱之爲管程。管程的示意圖如下:

clipboard.png

管程如何解決互斥

管程內部的數據結構,僅能被管程內部的過程所訪問,任何管程外的過程都不能訪問它;反之,局部於管程內部的過程也僅能訪問管程內的數據結構。由此可見,管程相當於圍牆,它把共享變量和對它進行操作的若干過程圍了起來,所有進程要訪問臨界資源時,都必須經過管程(相當於通過圍牆的門)才能進入,而管程每次只准許一個進程進入管程,從而實現了進程互斥。即當一個進程使用管程時,另一個進程必須等待。當一個進程使用完管程後,它必須釋放管程並喚醒等待管程的某一個進程

管程如何解決同步

管程裏引入了條件變量的概念來解決同步問題,而且每個條件變量都對應有一個等待隊列,條件變量對應的3個方法爲wait()、notify()、notifyAll()。示意圖如下:
圖片描述

線程先在入口等待隊列排隊進入管程,這確保了互斥訪問管程。當線程進入管程後,如果發現條件變量A不滿足,則需要調用A.wait()使線程進入A的條件變量等待隊列,此時其他在入口等待隊列的線程可以繼續進入管程。如果當條件變量A滿足條件時,則調用A.notify()或則A.notifyAll()(notify是通知等待隊列中的一個線程,notifyAll是通知所有線程)方法通知在A條件變量等待隊列中的線程,然後該線程會重新進入入口等待隊列中排隊進入管程。如此便實現了線程間的同步。
這與就醫流程相似,患者掛了號後,然後就到就診門口分診,等待叫號(類似入口等待隊列);當叫到自己的號時,患者就可以找大夫就診了,就診過程中,大夫開了個驗血的單子(類似條件等待隊列)那現在就需求去驗血的隊伍裏排隊,同時醫生可以給其他患者診治;等患者做完檢查,拿到檢查報告後需要重新排隊分診(線程重新進入入口等待隊列)。

總結

在併發編程領域,有兩大核心問題:互斥和同步,而這兩個問題,管程模型都可以解決。管程模型中利用入口等待隊列,保證同一時間只有一個線程進入管程,實現線程間的互斥;利用條件變量及條件變量等待隊列實現線程間的同步。Java中的synchronized以及lock都是管程模型的一種實現,瞭解了管程模型後,對後續深入學習Java併發大有好處。

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