簡單的說,notify()只喚醒一個正在等待的線程,當該線程執行完以后施放該對象的鎖,而沒有再次執行notify()方法,則其它正在等待的線程
則一直處於等待狀態,不會被喚醒而進入該對象的鎖的競爭池,就會發生死鎖。
JVM多個線程間的通信是通過 線程的鎖、條件語句、以及wait()、notify()/notifyAll組成。
下面來實現一個啟用多個線程來循環的輸出兩個不同的語句:
package com.tyxh.block;
class OutTurn {
private boolean isSub = true;
private int count = 0;
public synchronized void sub() {
try {
while (!isSub ) {
this.wait();
}
System.
out.println("sub ---- " + count);
isSub = false ;
this.notify();
} catch (Exception e) {
e.printStackTrace();
}
count++;
}
public synchronized void main() {
try {
while (isSub ) {
this.wait();
}
System.
out.println("main (((((((((((( " + count);
isSub = true ;
this.notify();
} catch (Exception e) {
e.printStackTrace();
}
count++;
}
}
|
package com.tyxh.block;
public class LockDemo {
public static void main(String[] args) {
// System.out.println("lock");
final OutTurn ot = new OutTurn();
for (int j = 0; j < 100; j++) {
new Thread(new Runnable() {
public void run() {
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
for (int i = 0; i < 5; i++) {
ot.sub();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
for (int i = 0; i < 5; i++) {
ot.main();
}
}
}).start();
}
}
}
|
解釋一下原因:
OutTurn類中的sub和main方法都是同步方法,所以多個調用sub和main方法的線程都會處於阻塞狀態,等待一個正在運行的線程來喚醒它們。下面分別分析一下使用notify和notifyAll方法喚醒線程的不同之處:
上面的代碼使用了notify方法進行喚醒,而notify方法只能喚醒一個線程,其它等待的線程仍然處於wait狀態,假設調用sub方法的線程執行完后(即System.
out .println("sub ---- " + count )執行完之后),所有的線程都處於等待狀態,此時在sub方法中的線程執行了isSub=false語句后又執行了notify方法,這時如果喚醒的是一個sub方法的調度線程,那么while循環等於true,則此喚醒的線程也會處於等待狀態,此時所有的線程都處於等待狀態,那么也就沒有了運行的線程來喚醒它們,這就發生了死鎖。
如果使用notifyAll方法來喚醒所有正在等待該鎖的線程,那么所有的線程都會處於運行前的准備狀態(就是sub方法執行完后,喚醒了所有等待該鎖的狀態,注:不是wait狀態),那么此時,即使再次喚醒一個sub方法調度線程,while循環等於true,喚醒的線程再次處於等待狀態,那么還會有其它的線程可以獲得鎖,進入運行狀態。
總結:notify方法很容易引起死鎖,除非你根據自己的程序設計,確定不會發生死鎖,notifyAll方法則是線程的安全喚醒方法。
附:
notify和notifyAll的區別:
notify()和notifyAll()都是Object對象用於通知處在等待該對象的線程的方法。
void notify(): 喚醒一個正在等待該對象的線程。
void notifyAll(): 喚醒所有正在等待該對象的線程。
void notifyAll(): 喚醒所有正在等待該對象的線程。
兩者的最大區別在於:
notifyAll使所有原來在該對象上等待被notify的線程統統退出wait的狀態,變成等待該對象上的鎖,一旦該對象被解鎖,他們就會去競爭。
notify他只是選擇一個wait狀態線程進行通知,並使它獲得該對象上的鎖,但不驚動其他同樣在等待被該對象notify的線程們,當第一個線程運行完畢以后釋放對象上的鎖,此時如果該對象沒有再次使用notify語句,即便該對象已經空閑,其他wait狀態等待的線程由於沒有得到該對象的通知,繼續處在wait狀態,直到這個對象發出一個notify或notifyAll,它們等待的是被notify或notifyAll,而不是鎖。