2013-03-23 3 views
2

Я получаю многопоточность Java. Я очень хорошо знаком с C/C++ pthreads, но у меня проблемы с функциями Java notify() и wait().Почему это вызывает исключение IllegalMonitorStateException?

Я понимаю, что IllegalMoinitorStateException вызывается только тогда, когда поток, который не имеет «собственного» (он же не синхронизирован), вызывает уведомление/ожидание.

При написании приложения я столкнулся с этой проблемой. Я изолировал проблему с помощью следующего кода теста:

public class HelloWorld 
{ 
    public static Integer notifier = 0; 
    public static void main(String[] args){ 
     notifier = 100; 
     Thread thread = new Thread(new Runnable(){ 
      public void run(){ 
        synchronized (notifier){ 
          System.out.println("Notifier is: " + notifier + " waiting"); 
          try{ 
           notifier.wait(); 
           System.out.println("Awake, notifier is " + notifier); 
          } 
          catch (InterruptedException e){e.printStackTrace();} 
        } 
      }}); 
     thread.start(); 
     try{ 
       Thread.sleep(1000); 
      } 
     catch (InterruptedException e){ 
       e.printStackTrace(); 
      } 
     synchronized (notifier){ 
      notifier = 50; 
      System.out.println("Notifier is: " + notifier + " notifying"); 
      notifier.notify(); 
     } 
     } 
    } 

Это выходы:

Exception in thread "main" java.lang.IllegalMonitorStateException 
     at java.lang.Object.notify(Native Method) 
     at HelloWorld.main(HelloWorld.java:27) 

Я считаю, что я уже приобрел замок на объекте уведомителя. Что я делаю не так?

Спасибо!

EDIT:

Из этого возможного дубликата (Synchronizing on an Integer value), кажется, что это не очень хорошая идея, чтобы синхронизировать на Integer, потому что трудно убедиться, что вы синхронизируете на том же экземпляре. Поскольку мое целое число, с которым я синхронизируюсь, является глобальным неподвижным статическим целым числом, почему я получаю разные экземпляры?

+0

Возможные дубликат: HTTP: //stackoverflow.com/questions/659915/synchronizing-on-an-integer-value – Cratylus

+0

Просто еще один намек, так как он уже был дан ответ: попробуйте установка уведомителя на конечный. Он не будет компилироваться, поскольку вы назначаете другое значение (объект) для уведомителя. –

ответ

5

Из-за notifier = 50; вы вызываете notifier.notify(); на другой объект.

0

Изначально, когда вы вызывали синхронизацию по notifier в основной теме, она ссылалась на объект в куче, содержимое которого составляло 0, поэтому поток принадлежит этому объекту. Теперь после того, как non-main thread помещается на wait с использованием notifier.wait, управление передается основной теме. Там после получения блокировки объекта Integer, содержащего значение 0, вы сделали notifier ссылкой на другой объект на кучевую память, содержащую значение 50, потому что notifier = 50; фактически эквивалентен notifier = new Integer(50), что означает, что создается новый объект Integer и его ссылка передана notifier. Теперь, когда поток видит notifier.notify, похоже, что основной поток теперь больше не владеет исходным объектом, который он ранее приобрел. Итак, IllegalMonitorStateException бросает.

0

Чтобы добавить дополнительную информацию, никогда не следует синхронизировать с не конечным объектом.

Вам необходимо синхронизировать постоянный объект. Если вы синхронизированы на любом объекте, который вы назначаете (т. Е. Меняете объект), тогда объект не является постоянным, и после того, как он назначит объект и попытается уведомить об этом, вы получите IllegalMonitorStateException. Это также означает, что, поскольку они синхронизируются на разных объектах, несколько потоков будут входить в защищенный блок одновременно, и условия гонки будут происходить.

Поскольку мое целое число, на которое я синхронизируюсь, является глобальным неподвижным статическим целым, почему я получаю разные экземпляры?

При присвоении значения к Integer он изменяет его объектную ссылку на разные - даже если это static. Вы являетесь не, изменяя тот же объект, потому что Integer не может быть мутирован.Таким образом, notifier = 50; присваивает его другому объекту, чем notifier = 0;.

Если вы хотите использовать, например, постоянный объект, который вы можете использовать AtomicBoolean:

public static final AtomicInteger notifier = new AtomicInteger(0); 
    ... 
    synchronize (notifier) { 
     notifier.set(50); 
     ... 
    } 

Здесь AtomicInteger могут быть отмечены final, потому что это тот же объект все время.

Для получения дополнительной информации см: Why is it not a good practice to synchronize on Boolean?

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