(轉(zhuǎn)載自 https://www.cnblogs.com/LeeScofiled/p/7225562.html)
synchronized
JAVA多線程這一塊有點(diǎn)繞,特別是對(duì)于鎖,對(duì)鎖機(jī)制理解不清的話,程序出現(xiàn)了問題也很難找到原因,在此記錄一下線程的執(zhí)行以及各種鎖。
1、JAVA中,每個(gè)對(duì)象有且只有一把鎖(lock),也叫監(jiān)視器(monitor)。
2、同步(synchronized),synchronized可以修飾的方法或方法中的對(duì)象。
3、如果有一個(gè)線程進(jìn)入到了synchronized方法修飾的對(duì)象,那么它將會(huì)獲得這個(gè)對(duì)象的唯一一把鎖,在該線程沒有交出這把鎖的時(shí)候,其它線程是無法訪問到該方法中的。該線程會(huì)在執(zhí)行完synchronized方法塊中的內(nèi)容后交出對(duì)象鎖。

4、關(guān)于wait,notify,屬于Object類,并且無法被重寫,(網(wǎng)上JDK的1.5和1.6有中文版的API,對(duì)于這一塊的翻譯基本都是機(jī)器翻譯,很不準(zhǔn)確。建議看原版的英文說明文檔)。
wait:
4.1、wait方法The current thread must own this object's monitor,當(dāng)前線程必需獲得這個(gè)對(duì)象的鎖。因?yàn)橐粋€(gè)線程進(jìn)入了synchronized的代碼塊表示這個(gè)線程拿到了對(duì)象鎖,那么這個(gè)wait方法必需在synchronized代碼塊中。
4.2、這個(gè)方法讓進(jìn)入到此處的線程丟掉對(duì)象鎖并且掛起等待(能執(zhí)行到wait方法的線程一定是拿到了對(duì)象鎖的線程,如果不理解,請(qǐng)看4.1)。
4.3、其它線程調(diào)用這個(gè)對(duì)象的notify或notifyAll方法時(shí),系統(tǒng)會(huì)在當(dāng)前掛起等待在wait方法處的多個(gè)線程中,隨機(jī)找出一個(gè)喚醒,被喚醒的線程會(huì)等待直到它拿到了對(duì)象鎖并繼續(xù)執(zhí)行。
4.4、一個(gè)線程可能在通知、打斷或超時(shí)之前被喚醒,這就是所謂的超時(shí)欺騙喚醒。實(shí)際情況下會(huì)很少出現(xiàn)這種情況,應(yīng)用程序必需防范著判斷條件使該線程被喚醒,如果條件不滿足需要該線程繼續(xù)等待。換句話說,wait方法必需放在循環(huán)里面。像下面這樣。
synchronized (obj) {
while (<condition does not hold>)
obj.wait(timeout);
... // Perform action appropriate to condition
}
這句話即使看明白了,也有些難理解,畫個(gè)圖:
4.4.1、比如有線程1進(jìn)到了synchronzied,拿到了對(duì)象鎖,此時(shí)其它線程都無法進(jìn)法該synchronized方法塊中。

4.4.2、當(dāng)線程1執(zhí)行到wait方法時(shí),會(huì)丟掉手上的鎖,這時(shí)其它線程就能進(jìn)來了,然后線程1會(huì)在此處掛起,不再執(zhí)行,直到有其它線程調(diào)用了這個(gè)對(duì)象的notify或notifyAll方法。

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

4.4.4、然后又來了一個(gè)線程4,進(jìn)入了該對(duì)象的另一個(gè)方法,并且調(diào)用了該對(duì)象的notify方法。

4.4.5、對(duì)象notify被調(diào)用后,wait方法處等待的線程中有一個(gè)有機(jī)會(huì)被喚醒,得到鎖并繼續(xù)往下執(zhí)行。其它線程依舊會(huì)在原處等待。

notify:
Wakes up a single thread that is waiting on this object's monitor 喚醒一個(gè)在wait方法處等待的線程。
The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. 這個(gè)被喚醒的線程不會(huì)被執(zhí)行,直到它得到了這個(gè)對(duì)象鎖。
The awakened thread will compete in the usual manner with any other threads that might be actively competing to synchronize on this object 被喚醒的線程將會(huì)與其它線程去競爭對(duì)象鎖。(表示被喚醒不代表著馬上會(huì)執(zhí)行,除非該線程拿到對(duì)象鎖。)
Only one thread at a time can own an object's monitor.只有一個(gè)線程在這此時(shí)得到這個(gè)對(duì)象鎖。
This method should only be called by a thread that is the owner of this object's monitor 這個(gè)方法需要被獲得了對(duì)象鎖的線程去調(diào)用。
獲取對(duì)象鎖有三種方法:
By executing a synchronized instance method of that object 執(zhí)行了對(duì)象的同步方法。
By executing the body of a {@code synchronized} statement that synchronizes on the object 執(zhí)行了這個(gè)對(duì)象同步代碼塊。
For objects of type {@code Class,} by executing a synchronized static method of that class.對(duì)于Class,執(zhí)行了一個(gè)靜態(tài)的synchronized方法(這表示鎖住了class)。
最后解釋一下關(guān)于下面這段代碼,文檔中為什么要我們用while,而不是if 或是不加判斷之類的
synchronized (obj) {
while (<condition does not hold>)
obj.wait(timeout);
... // Perform action appropriate to condition
}
在上面的例子中,如果這里的條件是
if(num == 1){
wait();
}
num++;
notify();
如果三個(gè)線程進(jìn)到wait方法時(shí)num為0,
我需要num 為 1時(shí),一直等待,直到它為0才能讓num+1
線程2獲得了鎖,當(dāng)它執(zhí)行到notify方法時(shí),num變成了1,此時(shí)如果線程1被喚醒得到對(duì)象鎖后,應(yīng)該是重新做判斷num == 1,如果num為1,還是得繼續(xù)等待掛起。
如果這里用的是if,那么它表示該線程1在進(jìn)if 前是滿足num ==1這個(gè)條件的,但是出if判斷時(shí)卻是不滿足num ==1這個(gè)條件的。根據(jù)已知我需要num 為 1時(shí),一直等待,所以這里的if判斷是不對(duì)的。
如果這里換成while,那么當(dāng)線程1被喚醒得到線程鎖,它還是得重新做判斷,如果num == 1那么這個(gè)線程不將繼續(xù)等待。