Java Wait and Notify: IllegalMonitorStateException

JavaMultithreadingConcurrency

Java Problem Overview


I don't completely understand how wait and notify (of Object) work, and as a result I'm forced to slim down my attempts into the following section of code.

Main.java:

import java.util.ArrayList;

class Main
{
  public static Main main = null;
  
  public static int numRunners = 4;
  public static ArrayList<Runner> runners = null;
  
  public static void main(String[] args)
  {
    main = new Main();
  }
  
  Main()
  {
    runners = new ArrayList<Runner>(numRunners);
    
    for (int i = 0; i < numRunners; i++)
    {
      Runner r = new Runner();
      runners.add(r);
      new Thread(r).start();
    }
    
    System.out.println("Runners ready.");
    notifyAll();
  }
}

Runner.java:

class Runner implements Runnable
{
  public void run()
  {
    try
    {
      Main.main.wait();
    } catch (InterruptedException e) {}
    System.out.println("Runner away!");
  }
}

Currently I get an IllegalMonitorStateException when calling Main.main.wait();, but I don't understand why. From what I can see, I need to synchronize Runner.run, but in doing so I assume it would only notify one thread, when the idea is to notify them all.

I've looked at java.util.concurrent, but I can't find a suitable replacement (maybe I'm just missing something).

Java Solutions


Solution 1 - Java

You can't wait() on an object unless the current thread owns that object's monitor. To do that, you must synchronize on it:

class Runner implements Runnable
{
  public void run()
  {
    try
    {
      synchronized(Main.main) {
        Main.main.wait();
      }
    } catch (InterruptedException e) {}
    System.out.println("Runner away!");
  }
}

The same rule applies to notify()/notifyAll() as well.

The Javadocs for wait() mention this:

> This method should only be called by a thread that is the owner of this object's monitor. See the notify method for a description of the ways in which a thread can become the owner of a monitor. > > #### Throws: > IllegalMonitorStateException – if the current thread is not the owner of this object's monitor.

And from notify():

> A thread becomes the owner of the object's monitor in one of three > ways: > > - By executing a synchronized instance method of that object. > - By executing the body of a synchronized statement that synchronizes on the object. > - For objects of type Class, by executing a synchronized static method of that class. >

Solution 2 - Java

You're calling both wait and notifyAll without using a synchronized block. In both cases the calling thread must own the lock on the monitor you call the method on.

From the docs for notify (wait and notifyAll have similar documentation but refer to notify for the fullest description):

> This method should only be called by a thread that is the owner of this object's monitor. A thread becomes the owner of the object's monitor in one of three ways: > > - By executing a synchronized instance method of that object. > - By executing the body of a synchronized statement that synchronizes on the object. > - For objects of type Class, by executing a synchronized static method of that class. > > Only one thread at a time can own an object's monitor.

Only one thread will be able to actually exit wait at a time after notifyAll as they'll all have to acquire the same monitor again - but all will have been notified, so as soon as the first one then exits the synchronized block, the next will acquire the lock etc.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionskeggseView Question on Stackoverflow
Solution 1 - JavadlevView Answer on Stackoverflow
Solution 2 - JavaJon SkeetView Answer on Stackoverflow