Monitor Object 模式
Monitor 其實(shí)是一種同步工具,或者說是同步機(jī)制,它通常被描述成一個(gè)對象,主要特點(diǎn)是:
- 同步。對象內(nèi)的所有方法都互斥的執(zhí)行。好比一個(gè) Monitor 只有一個(gè)運(yùn)行許可,任一個(gè)線程進(jìn)入任何一個(gè)方法都需要獲得這個(gè)許可,離開時(shí)把許可歸還。
- 協(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é)作過程:
- 同步方法的調(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)視鎖自動釋放。
- 同步方法線程掛起。如果調(diào)用同步方法的線程必須被阻塞或是其他原因不能立刻進(jìn)行,它能夠在一個(gè)監(jiān)視條件上等待,這將導(dǎo)致該客戶線程暫時(shí)釋放監(jiān)視鎖,并掛起(WAITING / TIMED_WAITING)在監(jiān)視條件上。
- 監(jiān)視條件通知。線程能夠通知一個(gè)監(jiān)視條件,目的是為了讓一個(gè)前期使自己掛起在一個(gè)監(jiān)視條件上的同步方法線程恢復(fù)運(yùn)行。
- 同步方法線程回復(fù)。一旦早先掛起在某監(jiān)視條件上的同步方法線程獲取通知,它將繼續(xù)在最初的等待監(jiān)視條件的點(diǎn)上執(zhí)行,執(zhí)行的條件是喚醒后搶占到監(jiān)視鎖。
下圖描述了監(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ī)理:

- 線程進(jìn)入同步方法中:
- 為了繼續(xù)執(zhí)行臨界區(qū)代碼,線程必須獲取 Monitor 鎖。如果獲取鎖成功,將成為該監(jiān)視者對象的擁有者。任一時(shí)刻內(nèi),監(jiān)視者對象只屬于一個(gè)活動線程(The Owner)
- 擁有監(jiān)視者對象的線程可以調(diào)用 wait() 進(jìn)入等待集合(Wait Set),同時(shí)釋放監(jiān)視鎖,進(jìn)入等待狀態(tài)。
- 其他線程調(diào)用 notify() / notifyAll() 接口喚醒等待集合中的線程,這些等待的線程需要重新獲取監(jiān)視鎖后才能執(zhí)行 wait() 之后的代碼。
- 同步方法執(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();
}
}
- wait 必須在 synchronized 方法或代碼塊中
- 用 while 而不是 if 判斷監(jiān)視條件,因?yàn)楫?dāng)線程被喚醒的時(shí)候,監(jiān)視條件可能根本沒有被滿足。如當(dāng)前線程調(diào)用的是 notifyAll
- lock可以是this
notify 是從條件隊(duì)列從隨機(jī)選擇一個(gè)線程來喚醒,而 notifyAll 是喚醒條件隊(duì)列中的所有線程。
只有同時(shí)滿足下列兩個(gè)條件時(shí),才使用 notify 而不是 notifyAll:
所有的等待線程類型相同:只有一個(gè)監(jiān)視條件與條件隊(duì)列相關(guān),并且每個(gè)線程從 wait 返回后將執(zhí)行相同的操作。
單進(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