下面是示例程序的一個版本,它改爲引入一個測試條件變量的循環。在從等待甦醒這樣你就避免對事物的狀態後一個線程壞的假設重新獲取一個鎖,而且也沒有兩個線程之間的順序關係:
public class W extends Thread {
long sum;
boolean done;
public static void main(String[] args) throws InterruptedException {
W w = new W();
w.start();
synchronized(w) {
while (!w.done) {
w.wait();
}
// move to within synchronized block so sum
// updated value is required to be visible
System.out.println(w.sum);
}
}
@Override public synchronized void run() {
for (int i = 0; i < 1000000; i++) {
sum += i;
}
done = true;
// no notify required here, see nitpick at end
}
}
這是不夠的等待通知,因爲你指出的原因(順序依賴性,你依賴的是競爭條件,希望一個線程在另一個線程之前獲取監視器)以及其他原因。首先,一個線程可以在沒有收到通知的情況下從等待中喚醒,但是你不能認爲有任何通知。
當一個線程等待時,它需要在一個循環中完成,在循環的測試中,它檢查一些條件。另一個線程應該設置該條件變量,以便第一個線程可以檢查它。 the Oracle tutorial的建議是:
注意:始終在測試等待條件的循環中調用wait。不要認爲中斷是針對您正在等待的特定情況,或者情況依然如此。
其他吹毛求疵:
當你的例子寫的,JVM不需要進行更改您的總和變量可見的主線程。如果添加一個同步實例方法來訪問sum變量,或者訪問同步塊內的和,那麼主線程將保證能夠看到sum的更新值。
看着你的日誌記錄,沒有什麼關於InterruptedException的嚴重問題,它並不意味着任何錯誤。當您在線程上調用中斷,設置其中斷標誌,並且該線程當前正在等待或正在休眠,或者在標誌仍然置位時進入等待或休眠方法時,會導致InterruptedException。在我的答案頂部的玩具示例中,我將異常置於throws子句中,因爲我知道這不會發生。
當線程終止時,它發出一個notifyAll,任何等待該對象的東西都會收到(同樣,這就是連接的實現方式)。部分原因是使用Runnable而不是Thread。
在這個特殊的例子中,在求和線程上調用Thread#join會更有意義,而不是調用wait。
這裏的例子重新編寫,使用加入代替:
public class J extends Thread {
private long sum;
synchronized long getSum() {return sum;}
public static void main(String[] args) throws InterruptedException {
J j = new J();
j.start();
j.join();
System.out.println(j.getSum());
}
@Override public synchronized void run() {
for (int i = 0; i < 1000000; i++) {
sum += i;
}
}
}
線程#加入呼叫等待,鎖定線程對象。當求和線程終止時,它發送一個通知並將其isAlive標誌設置爲false。同時在join方法中,主線程正在等待求和線程對象,它接收到通知,檢查isAlive標誌,並且實現它不必再等待,因此它可以離開join方法並打印結果。
從這些基本的API轉移到高級API,如java.util.concurrent.Executors和ExecutorService。查看代碼示例:http://examples.javacodegeeks.com/core-java/util/concurrent/executorservice/java-executorservice-example-tutorial/ –
@ sunrise76,java.util.concurrent中的類不是「更高級的:「他們在更高級別的抽象層面上運作。編寫生產代碼的devloper絕對應該使用更高級別的工具,但是_student_可以很好地理解構建這些更高級別工具的原語。就像學過彙編語言的人在用更高級的語言編寫時做出更明智的決定一樣,所以瞭解互斥鎖,條件變量和原子操作的人可以更好地使用隊列和線程池等。 –
您的理解是正確的。如果沒有其他線程在同一時間在'foo.wait()'調用中被阻塞,'foo.notify()'什麼也不做。 –