public class SpuriousWakeups {
private Object object = new Object();
public int count = 0;
public void get(int cnt) {
synchronized (object) {
if (count <= 0) {
try {
object.wait();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
count = count - cnt;
System.out.println(Thread.currentThread() + ": final " + count);
}
}
public void put(int cnt) {
synchronized (object) {
count = count + cnt;
object.notify();
}
}
public static void main(String[] args) throws InterruptedException {
SpuriousWakeups spuriousWakeups = new SpuriousWakeups();
Thread put1 = new Thread(new Runnable() {
@Override public void run() {
spuriousWakeups.put(1);
}
});
Thread get1 = new Thread(new Runnable() {
@Override public void run() {
spuriousWakeups.get(1);
}
});
Thread get2 = new Thread(new Runnable() {
@Override public void run() {
spuriousWakeups.get(1);
}
});
//get2.setPriority(9);
// get1先獲取, 讓object.wait 釋放object的monitor
get1.start();
Thread.sleep(100);
// 放入數據
put1.start();
get2.start();
}
}
執行的結果是:
1、
Thread[Thread-1,5,main]: final 0
get1獲取monitor往下執行,結束后釋放monitor;get2獲得get1釋放的monitor,其線程由BLOCKED狀態轉為WAITING狀態
2、
Thread[Thread-2,5,main]: final 0
Thread[Thread-1,5,main]: final -1
get2搶先獲得monitor執行完畢后,get1獲得get2釋放的monitor。至此線程全部執行完畢。
JDK推薦的寫法:
public final void wait()
throws InterruptedException導致當前線程等待,直到另一個線程調用該對象的notify()方法或notifyAll()方法。 換句話說,這個方法的行為就好像簡單地執行呼叫wait(0) 。
當前的線程必須擁有該對象的顯示器。 該線程釋放此監視器的所有權,並等待另一個線程通知等待該對象監視器的線程通過調用notify方法或notifyAll方法notifyAll 。 然后線程等待,直到它可以重新獲得監視器的所有權並恢復執行。
像在一個參數版本中,中斷和虛假喚醒是可能的,並且該方法應該始終在循環中使用:
synchronized (obj) {
while (<condition does not hold>)
obj.wait();
... // Perform action appropriate to condition
} 該方法只能由作為該對象的監視器的所有者的線程調用。 有關線程可以成為監視器所有者的方式的說明,請參閱notify方法。
線程狀態。 線程可以處於以下狀態:
NEW
尚未啟動的線程處於此狀態。
RUNNABLE
在Java虛擬機中執行的線程處於此狀態。
BLOCKED
被阻塞等待監視器鎖定的線程處於此狀態。
WAITING
正在等待另一個線程執行特定動作的線程處於此狀態。
TIMED_WAITING
正在等待另一個線程執行動作達到指定等待時間的線程處於此狀態。
TERMINATED
已退出的線程處於此狀態。
總結:
虛假喚醒原因:wait被notify后,線程由WAITING變成BLOCKED狀態,來競爭monitor,但是另外一個線程BLOCKED也會來競爭monitor,是沒法控制到底是誰先拿到monitor的。
如果不是wait的線程先拿到monitor,那當wait的線程拿到monitor的時候,共享的值已經改變了。