摘自http://www.javaworld.com.tw/jute/post/view?bid=5&id=72452&sty=1
大家好!我是Java新手!
我發現一個問題捏!
notifyAll()可以通知所有等待它的執行緒,
我測試用notify()的結果也會通知所有的執行緒捏!
請問它們最大的差別在哪呢?
執行環境:j2sdk1.4.2_05 + Eclipse3.0
package concurrent;
class Y extends Thread {
int total;
public void run() {
synchronized(this) {
while(total < 50) {
try {
Thread.sleep(500);
} catch(InterruptedException ie) {}
total++;
}
notify();
}
}
}
public class ClassOne implements Runnable {
Y ty;
ClassOne(Y ty) {
this.ty = ty;
}
public void run() {
synchronized(ty) {
try {
ty.wait();
} catch(InterruptedException ie) {}
System.out.println(Thread.currentThread().getName() + " Be notify()");
}
}
public static void main(String[] args) {
Y ty = new Y();
ClassOne x = new ClassOne(ty);
Thread t1 = new Thread(x, "t1");
Thread t2 = new Thread(x, "t2");
Thread t3 = new Thread(x, "t3");
t1.start();
t2.start();
t3.start();
ty.start();
}
}
經過我與其他版主的討論,這可以說是一個 bug 或是說這是一個令人意想不到 side effect(我沒留意到有哪份官方文件有提到這一點)。
原 因在於 Thread object 結束時(成為 dead thread)會(在自己身上)執行 notifyAll method,這一點可以從 Thread - join method 看出來(當一個 thread T1 invoke join(0) or join() 在另一個 thread object T2 身上,T1 thread 會 block 住,直到 thread T2 成為 non-alive thread)。
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
可以看到 Thread - join method 是以 synchronization 的機制來實做,當 join 的 actual argument value 為 0 是採用 wait(0) 來讓 caller thread block 住,wait(0) 執行後不會因為 timeout 過了而讓 caller thread wake up,可以想見 Thread class 的底層實做一定有在 run method 執行完畢而 Thread object 所啟動的 thread 成為 dead thread 時,有 invoke notifyAll 在 Thread object 身上,以便 wake up 其他因為 join(0) or join() invocation 而 block 住的 thread,否則其他的 thread 便一直停駐在紅色 的那一個 statement 處。
你試著把 Y - run method 中的 notify method call 拿掉,其他的 thread 還是會自動 wake up,在 ty thread 成為 dead thread 之後。
你把 Y instance 以 Thread 包裝起來執行,就可以避開這問題。
public class ClassOne implements Runnable {
public static void main(String[] args)
{
Y ty = new Y();
// [略] ...
new Thread(ty).start();
}
}