synchronized及wait,notify

來源:互聯網
上載者:User

方法控制對類成員變數的訪問:每個類執行個體對應一把鎖,每個 synchronized 方法都必須獲得調用該方法的類執行個體的鎖方能執行,否則所屬線程阻塞,方法一旦執行,就獨佔該鎖,直到從該方法返回時才將鎖釋放,此後被阻塞的線程方能獲得該鎖,重新進入可執行狀態。

wait()/notify():調用任意對象的 wait() 方法導致線程阻塞,並且該對象上的鎖被釋放。而調用 任意對象的notify()方法則導致因調用該對象的 wait() 方法而阻塞的線程中隨機播放的一個解除阻塞(但要等到獲得鎖後才真正可執行)。

synchronized和wait()、notify()的關係:

1.有synchronized的地方不一定有wait,notify

2.有wait,notify的地方必有synchronized.這是因為wait和notify不是屬於線程類,而是每一個對象都具有的方法,而且,這兩個方法都和對象鎖有關,有鎖的地方,必有synchronized。

另外,請注意一點:如果要把notify和wait方法放在一起用的話,必須先調用notify後調用wait,因為如果調用完wait,該線程就已經不是current thread了。

註:調用wait()方法前的判斷最好用while,而不用if;while可以實現被wakeup後thread再次作條件判斷;而if則只能判斷一次;

線程的四種狀態

  1. 新狀態:線程已被建立但尚未執行(start() 尚未被調用)。

  2. 可執行狀態:線程可以執行,雖然不一定正在執行。CPU 時間隨時可能被分配給該線程,從而使得它執行。

  3. 死亡狀態:正常情況下 run() 返回使得線程死亡。調用 stop()或 destroy() 亦有同樣效果,但是不被推薦,前者會產生異常,後者

是強制終止,不會釋放鎖。

  4. 阻塞狀態:線程不會被分配 CPU 時間,無法執行。

首先,前面敘述的所有方法都隸屬於 Thread 類,但是這一對 (wait()/notify()) 卻直接隸屬於 Object 類,也就是說,所有對象都擁有這一

對方法。初看起來這十分不可思議,但是實際上卻是很自然的,因為這一對方法阻塞時要釋放佔用的鎖,而鎖是任何對象都具有的,調用任意

對象的 wait() 方法導致線程阻塞,並且該對象上的鎖被釋放。

  而調用 任意對象的notify()方法則導致因調用該對象的 wait() 方法而阻塞的線程中隨機播放的一個解除阻塞(但要等到獲得鎖後才真正

可執行)。

  其次,前面敘述的所有方法都可在任何位置調用,但是這一對方法卻必須在 synchronized 方法或塊中調用,理由也很簡單,只有在

synchronized 方法或塊中當前線程才佔有鎖,才有鎖可以釋放。

  同樣的道理,調用這一對方法的對象上的鎖必須為當前線程所擁有,這樣才有鎖可以釋放。因此,這一對方法調用必須放置在這樣的

synchronized 方法或塊中,該方法或塊的上鎖對象就是調用這一對方法的對象。若不滿足這一條件,則程式雖然仍能編譯,但在運行時會出現

IllegalMonitorStateException 異常。

  wait() 和 notify() 方法的上述特性決定了它們經常和synchronized 方法或塊一起使用,將它們和作業系統的處理序間通訊機製作一個比

較就會發現它們的相似性:synchronized方法或塊提供了類似於作業系統原語的功能,它們的執行不會受到多線程機制的幹擾,而這一對方法

則相當於 block 和wakeup 原語(這一對方法均聲明為 synchronized)。

它們的結合使得我們可以實現作業系統上一系列精妙的處理序間通訊的演算法(如訊號量演算法),並用於解決各種複雜的線程間通訊問題。關於

wait() 和 notify() 方法最後再說明兩點:

  第一:調用 notify() 方法導致解除阻塞的線程是從因調用該對象的 wait() 方法而阻塞的線程中隨機選取的,我們無法預料哪一個線程

將會被選擇,所以編程時要特別小心,避免因這種不確定性而產生問題。

  第二:除了 notify(),還有一個方法 notifyAll() 也可起到類似作用,唯一的區別在於,調用 notifyAll() 方法將把因調用該對象的

wait() 方法而阻塞的所有線程一次性全部解除阻塞。當然,只有獲得鎖的那一個線程才能進入可執行狀態。

  談到阻塞,就不能不談一談死結,略一分析就能發現,suspend() 方法和不指定逾時期限的 wait() 方法的調用都可能產生死結。遺憾的

是,Java 並不在語言層級上支援死結的避免,我們在編程中必須小心地避免死結。

  以上我們對 Java 中實現線程阻塞的各種方法作了一番分析,我們重點分析了 wait() 和 notify()方法,因為它們的功能最強大,使用也

最靈活,但是這也導致了它們的效率較低,較容易出錯。實際使用中我們應該靈活使用各種方法,以便更好地達到我們的目的。

  七、守護線程

  守護線程是一類特殊的線程,它和普通線程的區別在於它並不是應用程式的核心部分,當一個應用程式的所有非守護線程終止運行時,即

使仍然有守護線程在運行,應用程式也將終止,反之,只要有一個非守護線程在運行,應用程式就不會終止。守護線程一般被用於在後台為其

它線程提供服務。

可以通過調用方法 isDaemon() 來判斷一個線程是否是守護線程,也可以調用方法 setDaemon() 來將一個線程設為守護線程。 

用synchronized關鍵字修飾方法後,程式將根據調用此方法的對象的鎖來判斷是否能調用此方法。

 

對一個類的instance method,則當此方法被一個線程調用時,其他線程不能再通過同一個對象調用此方法(可以通過這個類的另一個對象來調用這個方法)。

 

對一個類的static method,則當一個線程通過類對象調用此方法時,其他線程不能再通過類對象調用此方法。由於類對象在類載入時由虛擬機器建立,只有一個,所以同一時刻此方法只能被一個線程調用。

 

在servlet程式中,容器只執行個體化一個servlet對象,多個使用者訪問的是同一個servlet對象,因此對servlet的方法加同步修飾,可以防止多個使用者同時調用一個方法,避免共用衝突。

 

建立多線程程式時,在子線程中通過一個對象調用一個類的instance方法時,應該在主線程建立這個對象,將對象的引用通過子線程的建構函式或其他介面方法傳入子線程,供子線程使用。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.