Calling wait, notify, and notifyAll within a non-synchronized method

You may be used to seeing the wait(), notify(), and notifyAll() methods called within synchronized methods. But here's a subtlety that you may not know: The caller of the wait(), notify(), and notifyAll() methods is required to own the monitor for which it's invoking these methods. This point sometimes goes unnoticed because programmers are used to calling these methods from within synchronized methods.

You may be used to seeing the wait(), notify(), and notifyAll() methods called within synchronized methods. But here's a subtlety that you may not know: The caller of the wait(), notify(), and notifyAll() methods is required to own the monitor for which it's invoking these methods. This point sometimes goes unnoticed because programmers are used to calling these methods from within synchronized methods.

If you need to call wait(), notify(), or notifyAll() from within a non-synchronized method, then you must first obtain a lock on the object's monitor. If you don't, an exception will be generated when an attempt is made to call the method in question.

Here's an example of a class that manipulates Long objects and a class that supplies Longs to be manipulated:

import java.util.List;
import java.util.ArrayList;

public class LockTip extends Thread {
    private List longs = new ArrayList();

    public static void main(String args[]) {
        LockTip lt = new LockTip();
        lt.start();

        new LongSupplier(lt).start();
    }

    public void run() {
        while (true) {
            try {
                wait();

                // do something with longs
                System.out.println("doing something: " + this.longs);
            }
            catch (InterruptedException e) {}
        }
    }

    public void addLong(Long l) {
         this.longs.add(l);
         notifyAll();
    }
}

class LongSupplier extends Thread {
    private LockTip lt;

    public LongSupplier(LockTip lt) {
        this.lt = lt;
    }


    public void run() {
        while (true) {
            try {
                Thread.sleep(1000);
                this.lt.addLong(new Long(123));
            }
            catch (InterruptedException e) {}
        }
    }
}

This code will compile and run, but the first time there's an attempt to execute the wait() or notifyAll() methods, an exception will be thrown:

java.lang.IllegalMonitorStateException: current thread not owner
        at java.lang.Object.wait(Native Method)
        at java.lang.Object.wait(Object.java:426)
        at LockTip.run(LockTip.java:18)

While the fix for this problem is simple, it isn't necessarily obvious. You have two options: add the keyword synchronized to the method definition or synchronize on the "this" keyword. In this case, either option is equally valid.

The following code has been modified using the second option, synchronizing on "this":

import java.util.List;
import java.util.ArrayList;

public class LockTip extends Thread {
    private List longs = new ArrayList();

    public static void main(String args[]) {
        LockTip lt = new LockTip();
        lt.start();

        new LongSupplier(lt).start();
    }

    public void run() {
        while (true) {
            try {
                synchronized(this) {
                    wait();
                }
                // do something with longs
                System.out.println("doing something: " + this.longs);

            }
            catch (InterruptedException e) {}
        }
    }

    public void addLong(Long l) {
        synchronized(this) {
            this.longs.add(l);
            notifyAll();
        }
    }
}


class LongSupplier extends Thread {
    private LockTip lt;

    public LongSupplier(LockTip lt) {
        this.lt = lt;
    }


    public void run() {
        while (true) {
            try {
                Thread.sleep(1000);
                this.lt.addLong(new Long(123));
            }
            catch (InterruptedException e) {}
        }
    }
}

Now when the methods are called no exception is thrown.

Locks and monitors are important topics in multi-threaded programming. The Java language was designed with threads in mind, but programmers still have to make choices. Read more about it in the Javadoc for java.lang.Thread.

David Petersheim is the Director of Application Development with Genscape, Inc. He designs and develops server-side applications to acquire and process real-time energy data.