關於synchronized的幾點說明

釋放同步監視器的時機


任何線程進入同步代碼塊,同步方法之前,必須先獲得對同步監視器的鎖定。由於程序無法顯式釋放對同步監視器的鎖定,那麼究竟何時會釋放同步監視器鎖呢?
  • 當前線程的同步代碼塊或同步方法正常執行結束;
  • 當前線程在同步代碼塊或同步方法中遇到break或return語句返回;
  • 當前線程在執行同步代碼塊或同步方法時發生了未處理的Error或Exception,導致程序異常結束;
  • 當前線程在同步代碼塊或同步方法中調用wait()方法。
在如下所示的情況下,線程不會釋放同步監視器鎖:
  • 線程在同步代碼塊或同步方法中調用Thread.sleep()或Thread.yield()方法來暫停當前線程的執行;
  • 線程在同步代碼塊或同步方法中調用suspend() 方法掛起當前線程(當然,我們應該儘量避免使用suspend()和resume()方法來控制線程)。

wait()


Object類中有5個方法組成了等待/通知機制的核心:notify()、notifyAll()、wait()、wait(long)和wait(long,int)。在Java中,所有的類都從Object類t繼承而來,因此所有的類都擁有這些公有方法可供使用。而且,由於它們都被聲明爲final,因此在子類中不能覆蓋它們中的任何一個方法。
<span style="font-size:14px;">public final void wait() throws InterruptedException, IllegalMonitorStateException</span>
該方法用來將當前線程置入休眠狀態,直到接到通知或被中斷爲止。在調用wait()之前,線程必須要獲得該對象的對象級別鎖,即只能在同步方法或同步塊中調用wait()方法。調用wait()方法後當前線程會釋放鎖。在從wait()返回前,當前線程與其他線程競爭重新獲得鎖。如果調用wait()方法時,沒有持有適當的鎖,則拋出IllegalMonitorStateException,它是RuntimeException的一個子類,因此不需要try-catch結構。

notify()


public final native void notify() throws IllegalMonitorStateException
該方法也要在同步方法或同步塊中調用,如果調用notify()時沒有持有適當的鎖,也會拋出IllegalMonitorStateException。
notify()方法用來通知那些可能等待該對象級別鎖的其他線程。如果有多個線程在等待同一把鎖,則線程規劃器會任意挑選出其中一個wait()狀態的線程來發出通知,並使它等待獲取該對象的對象級別鎖。注意,調用notify()方法後,當前線程不會馬上釋放該對象級別鎖,必須要等到線程退出同步代碼塊或同步方法後纔會釋放對象級別鎖,wait狀態的線程纔可以獲取該對象鎖。
當第一個獲得了該對象鎖的wait線程運行完畢以後,它會釋放掉該對象鎖,此時如果該對象沒有再次調用notify()方法,則即便同步鎖已經空閒,其他wait狀態的線程也不會受到該對象的通知,會繼續阻塞在wait狀態,直到這個對象發出一個notify或notifyAll通知。這裏需要注意的是,它們等待的是被notify或notifyAll,而不是鎖,這與下面的notifyAll()方法執行後的情況不同。

notifyAll()


public final native void notifyAll() throws IllegalMonitorStateException
該方法與notify()方法的工作方式基本相同,不同的地方在於notifyAll()會使所有原來在該對象上wait的線程統統退出wait的狀態(即全部被喚醒,不再等待notify或notifyAll,但由於此時還沒有獲取到該對象鎖,因此還不能繼續往下執行),變成等待獲取該對象上的對象級別鎖,一旦該對象鎖被釋放,他們就會立即去競爭。如果其中一個線程獲得了該對象鎖,它就會繼續往下執行,其他線程繼續等待。在該線程退出同步代碼塊或同步方法釋放鎖後,其他的已經被喚醒的線程將會繼續競爭獲取該對象鎖,一直進行下去,直到所有被喚醒的線程都執行完畢。

wait(long)和wait(long, int)


顯然,這兩個方法是設置等待超時時間的,後者在超值時間上加上ns,精度也難以達到,因此該方法很少使用。對於前者,如果線程在等待獲取對象鎖的過程中已經超過了指定的毫秒數,則它通過競爭重新獲得鎖,並從wait(long)返回。另外,需要知道,如果設置了超時時間,當wait返回時,我們不能確定它是因爲接到了通知還是因爲超時而返回的,因爲wait方法不會返回任何相關的信息。但一般可以通過設置標誌位來判斷,在notify之前改變標誌位的值,在wait方法後讀取該標誌位的值來判斷,當然爲了保證notify不被遺漏,我們還需要另外一個標誌位來循環判斷是否調用wait方法。


發佈了31 篇原創文章 · 獲贊 19 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章