Ваша программа не работает, потому что вы вызываете notifyAll()
на экземпляр Referee
, но делайте это внутри блока, синхронизированного по PrintRunner.class
. Вы можете позвонить по номеру notify()/notifyAll()
по объектам, на которых вы держите замок, иначе вы получите IllegalMonitorStateException
.
Но переключение на PrintRunner.class.notifyAll()
не поможет, потому что этот вызов будет влиять только на те потоки, которые ждут уведомления на объекте PrintRunner.class
, и у вас нет таких потоков. Ваши потоки ждут в определенных случаях, а не в самом классе. В результате, ваши Referee
потребности перебрать все ожидания PrintRunner
экземпляров и вызвать notify()
по каждому из них:
for(PrintRunner runner: runners) {
synchronized(runner) {
runner.notify();
}
}
Описанное решение будет работать, но имеет тот недостаток, что не справедливо для всех бегунов. Некоторые из них будут уведомлены раньше других.
Важное примечание: использование PrintRunner.class.wait()
и PrintRunner.class.notifyAll()
будет работать, но будет иметь такую же несправедливость вопрос, потому что каждый бегун будет повторно захватить замок на одной PrintRunner.class
например, для того, чтобы добиться прогресса. И они могут делать это только последовательно. В вашем случае (у вас ничего нет в синхронизированном блоке, кроме вызова wait()
), задержка между запусками будет довольно незначительной, но все же она будет существовать.
К счастью, Java имеет лучшее решение для вашей проблемы – CountDownLatch
класса. В частности, вам понадобится CountDownLatch
со значением 1. Все ваши бегуны должны ждать на этой защелке, пока судья не установит ее на ноль. В этот момент все они будут выпущены сразу. Обратите внимание, что все объекты (судья и бегуны) должны использовать одну и ту же общую защелку. Пусть судья его владельцем (это пистолет):
Referee.java
private final CountDownLatch startLatch = new CountDownLatch(1);
public CountDownLatch getStartLatch() {
return startLatch;
}
@Override
public void run() {
// prepare to start, make sure all the runners are ready
// ...
// This will release all the waiting runners:
startLatch.countDown();
}
PrintRunner.java
@Override
public void run() {
try {
// This call will wait until the referee counts the latch down to zero
referee.getStartLatch().await();
} catch (InterruptedException e) {
// Handle unexpected interruption here
}
}
Для того, чтобы убедиться, что все бегуна нити и вы можете использовать другую защелку с начальным значением, равным числу потоков. Каждая бегущая нить должна отсчитывать эту защелку, как только она будет готова, прямо перед вызовом getStartLatch().await()
.Судья должен подождать на этой защелке, прежде чем считать начальную защелку. Это гарантирует, что ваша гонка будет как можно более справедливой. – все бегуны успевают подготовиться к ней, и все они будут выпущены в одно и то же время.
@jameslarge, я согласен. Вот почему мой ответ содержит абсолютно то же замечание и более справедливое решение с защелкой. –
Упс! Извините, я до конца не читал. Комментарий втянут. –