В вашем коде, похоже, нет гарантии, что при выполнении sync.notifyAll()
все потоки Cell дойдут до sync.wait()
.Это относится к последнему потоку Cell (пятому в вашем примере), который должен захватить блокировку на sync
, чтобы дождаться ее.Но поток Simulation также пытается сделать то же самое, не убедившись, что все ждут.Это состояние гонки заставляет симуляцию иногда захватывать блокировку до того, как последняя ячейка сможет сделать то же самое и ждать.
Поскольку эта последняя ячейка не ждет, она не получает уведомления, поэтому вся вещь застревает.Вы можете проверить это, добавив System.out.println () в качестве первой строки в каждом блоке synchronized (sync)
и написав соответственно «ожидание синхронизации» и «уведомление синхронизации».Когда вы уведомите об этом, вы увидите, что только 4 потока ждут синхронизации.
Чтобы все ожидали, когда симулятор уведомит вас, вложите два синхронизированных блока в Cell#run()
:
public class Counter {
private int value;
public Counter(int value) {
this.value = value;
}
public void setValue(int value) {
this.value = value;
}
public void decrement() {
this.value--;
}
public int getValue() {
return this.value;
}
public static void main(String[] args) {
new Simulation().start();
}
}
class Cell extends Thread {
private Object sync;
private Counter counter;
public Cell(Object sync, Counter counter) {
this.sync = sync;
this.counter = counter;
}
public void run() {
for (int r = 0; r < Simulation.ROUND_NUM; r++) {
// do something
synchronized (sync) {
synchronized (counter) {
counter.decrement();
counter.notifyAll();
}
try {
sync.wait();
} catch (Exception ignored) {}
}
}
}
}
class Simulation extends Thread {
public static final int THREAD_NUM = 900;
public static final int ROUND_NUM = 30;
public Object sync = new Object();
private Counter counter = new Counter(THREAD_NUM);
public void run() {
for (int i = 0; i < THREAD_NUM; i++) {
Cell c = new Cell(sync, counter);
c.start();
}
for (int i = 0; i < ROUND_NUM; i++) {
synchronized (counter) {
while (counter.getValue() != 0) {
try {
counter.wait();
} catch (Exception ex) {
}
}
counter.setValue(THREAD_NUM);
}
synchronized (sync) {
sync.notifyAll();
}
}
}
}