56

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).

skeggse
  • 6,103
  • 11
  • 57
  • 81
  • 2
    You should never use wait and notify. They were necessary with the old Java, but now they should only be used by people who write libraries. You should now use the facilities in java.util.concurrent. – toto2 Aug 19 '11 at 20:25
  • Agree with toto2, even after you add synchronization this code will not work. What happens if main calls notifyAll before all the other threads reach wait()? – Michael Krussel Aug 19 '11 at 20:33

2 Answers2

83

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.
Rory O'Kane
  • 29,210
  • 11
  • 96
  • 131
dlev
  • 48,024
  • 5
  • 125
  • 132
  • 5
    wait() would release object monitor before blocking its thread, so it would not block other threads calling notify. – gonglong Jan 04 '15 at 09:00
  • 1
    For people linked here, the object you call `wait()` on must be the same object you're synchronizing on. Calling `x.wait()` in a `synchronized(y)` block will yield the same exception. – user695022 Sep 13 '17 at 15:11
12

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.

Community
  • 1
  • 1
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194