Calling wait, notify, and notifyAll within a non-synchronized method
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.