我在玩 Java 同步機制,遇到了我無法解釋的情況。
在下面的代碼示例中,我有一個簡單的應用程式,它正在生成一些執行緒,這些執行緒正在等待來自物件的信號,并在收到通知后相互通知:
public class Main {
public static final Object o = new Object();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 6; i) {
new Thread(() -> {
try {
synchronized (o) {
o.wait();
System.out.println("Some work");
o.notify();
}
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() " notified someone");
}).start();
}
synchronized (o) {
o.notify();
}
}
}
并且大多數時候應用程式的最后創建執行緒掛起:回應是:
Some work
Some work
Some work
Some work
Some work
Thread-3 notified someone
Thread-4 notified someone
Thread-0 notified someone
Thread-2 notified someone
Thread-1 notified someone
在應用試錯技術后,我發現如果我Thread.sleep(100);在主執行緒應用程式中的回圈和通知之間暫停,就會開始正常作業。
經過調查,我發現從 main 方法發送通知后執行緒狀態是:
main "Thread-0" prio=5 Id=13 RUNNABLE
main "Thread-1" prio=5 Id=14 WAITING on java.lang.Object@2133c8f8 owned by "Thread-0" Id=13
main "Thread-2" prio=5 Id=15 WAITING on java.lang.Object@2133c8f8 owned by "Thread-0" Id=13
main "Thread-3" prio=5 Id=16 WAITING on java.lang.Object@2133c8f8 owned by "Thread-0" Id=13
main "Thread-4" prio=5 Id=17 WAITING on java.lang.Object@2133c8f8 owned by "Thread-0" Id=13
main "Thread-5" prio=5 Id=18 BLOCKED on java.lang.Object@2133c8f8 owned by "Thread-0" Id=13
在發送最后一個通知之前,狀態為:
Thread-4 "Thread-4" prio=5 Id=17 RUNNABLE
Thread-4 "Thread-5" prio=5 Id=18 BLOCKED on java.lang.Object@2133c8f8 owned by "Thread-4" Id=17
// Final state
"Thread-5" prio=5 Id=18 WAITING on java.lang.Object@2133c8f8
所以很明顯,最后一個執行緒從應用程式開始就被阻塞了,無法接收倒數第二個執行緒的通知,最終到達臨界區代碼并停留在等待線等待通知。
可能它最初被阻止是因為當他們都試圖獲得 o 鎖定時與 main 方法發生沖突。
如果更了解這種多執行緒技術的人能給我一些解釋或只是對這種行為的一些提示,那就太好了。
uj5u.com熱心網友回復:
問題在于當沒有其他執行緒在等待時呼叫通知。信號量和作業佇列突出顯示您缺少的內容
信號
public class Semaphore {
private int tickets;
public Semaphore (int tickets) {
this.tickets = ticket;
}
public synchronized void getTicket() {
if (tickets > 0) {
tickets--;
} else {
while (tickets == 0) {
wait();
}
tickets--;
}
}
public synchronized void releaseTicket() {
tickets ;
notify();
}
}
作業佇列的作業方式類似,但它有一個佇列,而不是工單Runnables。當執行緒呼叫 getJob 時,如果佇列中有作業,它會將作業出列并將其交給執行緒。否則,執行緒等待作業。
uj5u.com熱心網友回復:
您注意到等待進入同步塊 (BLOCKED) 的執行緒和在 wait() 呼叫中阻塞的執行緒 (WAITING) 之間的區別。
您的回圈中有一個競爭條件。您創建執行緒并啟動它們。他們中的大多數人都會進入同步塊。這些執行緒中的每一個都到達一個同步塊,獲取鎖,并在等待時釋放它。
在您的主執行緒進入同步塊之前,最后一個執行緒沒有時間運行到同步塊。一旦你的 main 獲得了你的物件的鎖,通知一個等待鎖的執行緒。該執行緒喚醒并獲取鎖,而不讓最后一個執行緒進入同步塊。
前五個執行緒完成并通知,一個執行緒通知任何人。然后最后一個執行緒有機會進入同步塊并呼叫wait,但它永遠不會得到通知。
這就是 Thread.sleep 有幫助的原因,因為您的最后一個執行緒將有時間到達同步塊并等待。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/481899.html
下一篇:將Python嵌入到C 分段錯誤
