2016-08-28 13 views
3

Я хочу, чтобы имитировать гоночную игру многопоточными, и я хочу, чтобы бегуны начали бегать после Referee стрелять из пистолета, поэтому я положил wait() в метод runner's run, чтобы подождать для судьи, это метод бегун (PrintRunner.class) пробегИзвестить тему от другого класса, используя синхронизацию, но результат IllegalMonitorStateException

@Override 
public void run() { 
    sleepTime = random.nextInt(3)+8; 
    System.out.println(name+" is ready~"); 
    try { 
     synchronized(this){ 
      wait(); 
     } 
     System.out.println("Start running~"); 
     Thread.sleep(sleepTime*1000); 
    } catch (InterruptedException e) { 
     System.err.println(e.getMessage()); 
    } 
    System.out.println(name +" win the game!!!"); 
} 

И это Referee метод запуска:

@Override 
public void run() { 
    TimeUnit unit = TimeUnit.SECONDS; 
    try { 
     unit.sleep(3); 
     System.out.println(name+" : On your mark, get set~"); 
     unit.sleep(5); 
    } catch (InterruptedException e) { 
     System.err.println(e.getMessage()); 
    } 
    System.out.println("Fire a pistol!!!"); 

    synchronized(PrintRunner.class){ 
     notifyAll(); 
    } 
} 

я получаю IllegalMonitorStateException когда судья уведомляет бегунов, у меня был замок PrintRunner, когда я использовал wait() и notifyAll().

Пожалуйста, сообщите мне, почему код идет не так.

ответ

4

Ваша программа не работает, потому что вы вызываете 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().Судья должен подождать на этой защелке, прежде чем считать начальную защелку. Это гарантирует, что ваша гонка будет как можно более справедливой. – все бегуны успевают подготовиться к ней, и все они будут выпущены в одно и то же время.

+0

@jameslarge, я согласен. Вот почему мой ответ содержит абсолютно то же замечание и более справедливое решение с защелкой. –

+0

Упс! Извините, я до конца не читал. Комментарий втянут. –

 Смежные вопросы

  • Нет связанных вопросов^_^