Java 多線程(二)-Monitor

Monitor Object 模式

Monitor 其實(shí)是一種同步工具,或者說是同步機(jī)制,它通常被描述成一個(gè)對象,主要特點(diǎn)是:

  1. 同步。對象內(nèi)的所有方法都互斥的執(zhí)行。好比一個(gè) Monitor 只有一個(gè)運(yùn)行許可,任一個(gè)線程進(jìn)入任何一個(gè)方法都需要獲得這個(gè)許可,離開時(shí)把許可歸還。
  2. 協(xié)作。通常提供signal機(jī)制:允許正持有許可的線程暫時(shí)放棄許可,等待某個(gè)監(jiān)視條件成真,條件成立后,當(dāng)前線程可以通知正在等待這個(gè)條件的線程,讓它可以重新獲得運(yùn)行許可。
結(jié)構(gòu)

在 Monitor Object 模式中,主要有四種類型參與者:

監(jiān)視者對象 Monitor Object

負(fù)責(zé)公共的接口方法,這些公共的接口方法會在多線程的環(huán)境下被調(diào)用執(zhí)行。

同步方法

這些方法是監(jiān)視者對象所定義。為了防止競爭條件,無論是否有多個(gè)線程并發(fā)調(diào)用同步方法,還是監(jiān)視者對象還用多個(gè)同步方法,在任一事件內(nèi)只有一個(gè)同步方法能夠執(zhí)行。

監(jiān)控鎖 Monitor Lock

每一個(gè)監(jiān)視者對象都會擁有一把監(jiān)視鎖。

監(jiān)控條件 Monitor Condition

同步方法使用監(jiān)視鎖和監(jiān)視條件來決定方法是否需要阻塞或重新執(zhí)行。

執(zhí)行序列

在 Monitor Object 模式中,參與者之間將發(fā)生如下協(xié)作過程:

  1. 同步方法的調(diào)用和串行化。當(dāng)某線程調(diào)用監(jiān)視者對象的同步方法時(shí),必須首先獲得它的監(jiān)視鎖。只要監(jiān)視者對象有其他同步方法正在執(zhí)行,獲取操作便不會成功,該線程將處于阻塞(BLOCKED)狀態(tài)。當(dāng)線程獲得監(jiān)控鎖后,執(zhí)行方法實(shí)現(xiàn)服務(wù),一旦同步方法完成執(zhí)行,監(jiān)視鎖自動釋放。
  2. 同步方法線程掛起。如果調(diào)用同步方法的線程必須被阻塞或是其他原因不能立刻進(jìn)行,它能夠在一個(gè)監(jiān)視條件上等待,這將導(dǎo)致該客戶線程暫時(shí)釋放監(jiān)視鎖,并掛起(WAITING / TIMED_WAITING)在監(jiān)視條件上。
  3. 監(jiān)視條件通知。線程能夠通知一個(gè)監(jiān)視條件,目的是為了讓一個(gè)前期使自己掛起在一個(gè)監(jiān)視條件上的同步方法線程恢復(fù)運(yùn)行。
  4. 同步方法線程回復(fù)。一旦早先掛起在某監(jiān)視條件上的同步方法線程獲取通知,它將繼續(xù)在最初的等待監(jiān)視條件的點(diǎn)上執(zhí)行,執(zhí)行的條件是喚醒后搶占到監(jiān)視鎖。

下圖描述了監(jiān)視者對象的動態(tài)特性:

圖1 監(jiān)視者對象的動態(tài)特性

Java 中的Monitor

實(shí)質(zhì)上,Java 的 Object 類本身就是監(jiān)視者對象,Java 對于 Monitor Object 模式做了內(nèi)建的支持。

  • Object 類本身就是監(jiān)視者對象
  • 每個(gè) Object 都帶了一把看不見的鎖,通常叫 內(nèi)部鎖/Monitor 鎖/Instrinsic Lock, 這把鎖就是 監(jiān)控鎖
  • synchronized 關(guān)鍵字修飾方法和代碼塊就是 同步方法
  • wait()/notify()/notifyAll() 方法構(gòu)成監(jiān)控條件(Monitor Condition)

下圖描述了 Java Monitor 的工作機(jī)理:


圖2 Java Monitor工作機(jī)理
  1. 線程進(jìn)入同步方法中:
  2. 為了繼續(xù)執(zhí)行臨界區(qū)代碼,線程必須獲取 Monitor 鎖。如果獲取鎖成功,將成為該監(jiān)視者對象的擁有者。任一時(shí)刻內(nèi),監(jiān)視者對象只屬于一個(gè)活動線程(The Owner)
  3. 擁有監(jiān)視者對象的線程可以調(diào)用 wait() 進(jìn)入等待集合(Wait Set),同時(shí)釋放監(jiān)視鎖,進(jìn)入等待狀態(tài)。
  4. 其他線程調(diào)用 notify() / notifyAll() 接口喚醒等待集合中的線程,這些等待的線程需要重新獲取監(jiān)視鎖后才能執(zhí)行 wait() 之后的代碼。
  5. 同步方法執(zhí)行完畢了,線程退出臨界區(qū),并釋放監(jiān)視鎖。

關(guān)于 wait / notify / notifyAll

條件隊(duì)列

條件隊(duì)列(圖2 中的 Wait Set)存儲的是"處于等待狀態(tài)的線程",這些線程在等待某種特定的條件變成真。正如每個(gè)Java對象都可以作為一個(gè)鎖,每個(gè)對象同樣可以作為一個(gè)條件隊(duì)列,這個(gè)對象的wait /notify / notifyAll就構(gòu)成了內(nèi)部條件隊(duì)列的API。對象的監(jiān)控鎖與條件隊(duì)列是相互關(guān)聯(lián)的。要調(diào)用條件隊(duì)列的任何一個(gè)方法,必須先持有該對對象上的鎖,一個(gè)推論就是“wait / notify 方法只能出現(xiàn)在相應(yīng)的同步塊中”。如果不在同步方法或同步塊中,運(yùn)行時(shí)會報(bào)IllegalMonitorStateException。

實(shí)踐模式
void xxxMethod() throws InterruptedException{  
    synchronized(lock){  
        while(!conditionPredition)  
            lock.wait();  
        doSomething();  
    }  
}
  1. wait 必須在 synchronized 方法或代碼塊中
  2. 用 while 而不是 if 判斷監(jiān)視條件,因?yàn)楫?dāng)線程被喚醒的時(shí)候,監(jiān)視條件可能根本沒有被滿足。如當(dāng)前線程調(diào)用的是 notifyAll
  3. lock可以是this

notify 是從條件隊(duì)列從隨機(jī)選擇一個(gè)線程來喚醒,而 notifyAll 是喚醒條件隊(duì)列中的所有線程。

只有同時(shí)滿足下列兩個(gè)條件時(shí),才使用 notify 而不是 notifyAll:

  1. 所有的等待線程類型相同:只有一個(gè)監(jiān)視條件與條件隊(duì)列相關(guān),并且每個(gè)線程從 wait 返回后將執(zhí)行相同的操作。

  2. 單進(jìn)單出:條件變量上的每次通知,最多只能喚醒一個(gè)線程來執(zhí)行。

內(nèi)容來源

http://www.ibm.com/developerworks/cn/java/j-lo-synchronized/

http://www.cnblogs.com/tomsheep/archive/2010/06/09/1754419.html

http://yizhenn.iteye.com/blog/2308688

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容