[英]How to use wait and notify protocol with multiple threads
具體來說,有人可以告訴我這段代碼有什么問題。 它應該啟動線程,所以應該打印“輸入線程...”5次,然后等到調用notifyAll()。 但是,它會隨機打印“輸入......”和“完成......”,並且仍在等待其他人。
public class ThreadTest implements Runnable {
private int num;
private static Object obj = new Object();
ThreadTest(int n) {
num=n;
}
@Override
public void run() {
synchronized (obj) {
try {
System.out.println("Entering thread "+num);
obj.wait();
System.out.println("Done Thread "+num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Runnable tc;
Thread t;
for(int i=0;i<5;i++) {
tc = new ThreadTest(i);
t = new Thread(tc);
t.start();
}
synchronized (obj) {
obj.notifyAll();
}
}
}
你沒有對方法調用做任何明顯的錯誤,但是你有競爭條件 。
雖然在一個理想的世界中,主線程將在所有工作線程到達wait()調用后到達其synchronized塊, 但無法保證 (您明確告訴虛擬機您不希望線程按順序執行用主線程制作線程)。 可能會發生(例如,如果您只有一個核心)線程調度程序決定立即阻止所有工作線程,它們開始允許主線程繼續。 由於高速緩存未命中,工作線程可能是上下文切換出來的。 可能是一個工作線程阻塞I / O(print語句)並且主線程在其位置切換。
因此,如果主線程在所有工作線程到達wait()調用之前設法到達同步塊,則那些尚未到達wait()調用的工作線程將無法按預期運行。 由於當前設置不允許您控制此操作,因此必須添加對此的顯式處理。 您可以添加某種變量,當每個工作線程到達wait()並且主線程不調用notifyAll()直到此變量達到5,或者您可以讓主線程循環並重復調用notifyAll()時,會增加某種變量。以便工作線程在多個組中發布。
看一下java.util.concurrent
包 - 有幾個鎖類提供比基本同步鎖更強大的功能 - 一如既往,Java可以讓你免於重新發明輪子。 CountDownLatch似乎特別相關。
總之,並發很難 。 您必須設計以確保當線程在您不想要的訂單中執行時,一切仍然有效,以及您希望的訂單。
我是CountDownLatch推薦的第二個。 這是我用於多線程測試的模式:
final int threadCount = 200;
final CountDownLatch startPistol = new CountDownLatch(1);
final CountDownLatch startingLine = new CountDownLatch(threadCount);
final CountDownLatch finishingLine = new CountDownLatch(threadCount);
// Do a business method...
Runnable r = new Runnable() {
public void run() {
startingLine.countDown();
try {
startPistol.await();
// TODO: challenge the multithreadedness here
} catch (InterruptedException e) {
Thread.interrupted();
}
finishingLine.countDown();
}
};
// -- READY --
for (int i = 0; i < threadCount; i++) {
Thread t = new Thread(r);
t.start();
}
// Wait for the beans to reach the finish line
startingLine.await(1000, TimeUnit.MILLISECONDS);
// -- SET --
// TODO Assert no one has started yet
// -- GO --
startPistol.countDown(); // go
assertTrue(finishingLine.await(5000, TimeUnit.MILLISECONDS));
// -- DONE --
// TODO: final assert
我們的想法是保證線程將執行的下一行代碼是挑戰多線程或盡可能接近它的代碼。 我認為線程是賽道上的跑步者。 你讓他們在賽道上(創造/開始),等待他們在起跑線上完美排隊(每個人都叫做startingLine.countDown()),然后解雇起始手槍(startPistol.countDown())並等待所有人越過終點線(finishingLine.countDown())。
[編輯]應該注意的是,如果你沒有想要在startingLine.await()和startingPistol.countDown()之間執行任何代碼或檢查,你可以將startingLine和startingPistol組合成一個CyclicBarrier(threadCount + 1)。 雙重CountDownLatch方法實際上是相同的,並且允許主測試線程在所有其他線程排成一行並且在它們開始運行之前進行任何設置/檢查,如果需要的話。
您的代碼的根本問題是,在主線程調用notifyAll
之后 ,某些線程才會wait
。 所以當他們等待時,沒有什么能喚醒他們。
要使其工作(使用wait / notify),您需要同步主線程,以便它等待所有子線程進入可以在進行該調用之前接收通知的狀態。
一般來說,與wait
, notify
和primitive鎖同步是很棘手的。 在大多數情況下,通過使用Java並發實用程序類,您將獲得更好的結果(即更簡單,更可靠和更高效的代碼)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.