Java線程鎖,synchronized、wait、notify詳解


(原)

JAVA多線程這一塊有點繞,特別是對於鎖,對鎖機制理解不清的話,程序出現了問題也很難找到原因,在此記錄一下線程的執行以及各種鎖。

1、JAVA中,每個對象有且只有一把鎖(lock),也叫監視器(monitor)

2、同步(synchronized),synchronized可以修飾的方法或方法中的對象。

3、如果有一個線程進入到了synchronized方法修飾的對象,那么它將會獲得這個對象的唯一一把鎖,在該線程沒有交出這把鎖的時候,其它線程是無法訪問到該方法中的。該線程會在執行完synchronized方法塊中的內容后交出對象鎖。

 

 

4、關於wait,notify,屬於Object類,並且無法被重寫,(網上JDK的1.5和1.6有中文版的API,對於這一塊的翻譯基本都是機器翻譯,很不准確。建議看原版的英文說明文檔)。

wait:

4.1、wait方法The current thread must own this object's monitor,當前線程必需獲得這個對象的鎖。因為一個線程進入了synchronized的代碼塊表示這個線程拿到了對象鎖,那么這個wait方法必需在synchronized代碼塊中。

4.2、這個方法讓進入到此處的線程丟掉對象鎖並且掛起等待(能執行到wait方法的線程一定是拿到了對象鎖的線程,如果不理解,請看4.1)。

4.3、其它線程調用這個對象的notify或notifyAll方法時,系統會在當前掛起等待在wait方法處的多個線程中,隨機找出一個喚醒,被喚醒的線程會等待直到它拿到了對象鎖並繼續執行。

4.4、一個線程可能在通知、打斷或超時之前被喚醒,這就是所謂的超時欺騙喚醒。實際情況下會很少出現這種情況,應用程序必需防范着判斷條件使該線程被喚醒,如果條件不滿足需要該線程繼續等待。換句話說,wait方法必需放在循環里面。像下面這樣。

synchronized (obj) {
         while (<condition does not hold>)
             obj.wait(timeout);
         ... // Perform action appropriate to condition
     }

這句話即使看明白了,也有些難理解,畫個圖:
4.4.1、比如有線程1進到了synchronzied,拿到了對象鎖,此時其它線程都無法進法該synchronized方法塊中。

4.4.2、當線程1執行到wait方法時,會丟掉手上的鎖,這時其它線程就能進來了,然后線程1會在此處掛起,不再執行,直到有其它線程調用了這個對象的notify或notifyAll方法。

4.4.3、此時線程2和線程3以同樣的方法來到了wait方法處等待。

4.4.4、然后又來了一個線程4,進入了該對象的另一個方法,並且調用了該對象的notify方法。

4.4.5、對象notify被調用后,wait方法處等待的線程中有一個有機會被喚醒,得到鎖並繼續往下執行。其它線程依舊會在原處等待。

notify:

Wakes up a single thread that is waiting on this object's monitor 喚醒一個在wait方法處等待的線程。
The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. 這個被喚醒的線程不會被執行,直到它得到了這個對象鎖。
The awakened thread will compete in the usual manner with any other threads that might be actively competing to synchronize on this object 被喚醒的線程將會與其它線程去競爭對象鎖。(表示被喚醒不代表着馬上會執行,除非該線程拿到對象鎖。)
Only one thread at a time can own an object's monitor.只有一個線程在這此時得到這個對象鎖。

This method should only be called by a thread that is the owner of this object's monitor 這個方法需要被獲得了對象鎖的線程去調用。
獲取對象鎖有三種方法:
By executing a synchronized instance method of that object 執行了對象的同步方法。
By executing the body of a {@code synchronized} statement that synchronizes on the object 執行了這個對象同步代碼塊。
For objects of type {@code Class,} by executing a synchronized static method of that class.對於Class,執行了一個靜態的synchronized方法(這表示鎖住了class)。

最后解釋一下關於下面這段代碼,文檔中為什么要我們用while,而不是if 或是不加判斷之類的
synchronized (obj) {
         while (<condition does not hold>)
             obj.wait(timeout);
         ... // Perform action appropriate to condition
     }

 在上面的例子中,如果這里的條件是

if(num == 1){
  wait();  
}

num++;

notify();

如果三個線程進到wait方法時num為0,

我需要num 為 1時,一直等待,直到它為0才能讓num+1

線程2獲得了鎖,當它執行到notify方法時,num變成了1,此時如果線程1被喚醒得到對象鎖后,應該是重新做判斷num == 1,如果num為1,還是得繼續等待掛起。

如果這里用的是if,那么它表示該線程1在進if 前是滿足num ==1這個條件的,但是出if判斷時卻是不滿足num ==1這個條件的。根據已知我需要num 為 1時,一直等待,所以這里的if判斷是不對的。

如果這里換成while,那么當線程1被喚醒得到線程鎖,它還是得重新做判斷,如果num ==  1那么這個線程不將繼續等待。

 
        

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM