Reputation: 2111
I have this example code based on a sample I found on the web to illustrate how to use wait()
and notify()
. Let me preface the code sample with the following statement a Java textbook makes about wait
and notify
.
A thread can't invoke a wait or notify method on an object unless it owns that's object's lock
Now take a look at this code and the output:
public class ThreadTester
{
public class Message {
private String msg;
public Message(String str){ this.msg=str;}
public String getMsg() { return msg; }
public void setMsg(String str) { this.msg=str;}
}
public class Waiter implements Runnable{
private Message msg;
public Waiter(Message m){
this.msg = m;
}
public void run() {
String name = Thread.currentThread().getName();
synchronized (msg) {
try{
System.out.println(name + " acquired lock to msg object. waiting to get notified");
msg.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(name + " waiter done at time: " + System.currentTimeMillis());
}
System.out.println(name + " waiter giving up msg object lock");
}
}
public class Notifier implements Runnable {
private Message msg;
public Notifier(Message msg) {
this.msg = msg;
}
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name + " started");
try {
Thread.sleep(5000);
synchronized (msg) {
String localMesg = name + " acquired lock to msg object. Notifier work done.";
System.out.println(localMesg);
msg.setMsg(localMesg);
msg.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
void runtest() {
Message msg = new Message("process it");
Waiter waiter1 = new Waiter(msg);
new Thread(waiter1,"waiter1").start();
Waiter waiter2 = new Waiter(msg);
new Thread(waiter2, "waiter2").start();
Notifier notifier = new Notifier(msg);
new Thread(notifier, "notifier").start();
System.out.println("All the threads are started");
}
public static void main(String [] args) {
new ThreadTester().runtest();
}
}
Here is the output:
waiter1 acquired lock to msg object. waiting to get notified
waiter2 acquired lock to msg object. waiting to get notified
All the threads are started
notifier started
notifier acquired lock to msg object. Notifier work done.
waiter1 waiter done at time: 1419745413721
waiter1 waiter giving up msg object lock
Now the question is, how can waiter2
or notifier
threads acquire the msg object's lock when waiter1
thread is still holding it? That seems to directly contradict the Java textbook statement I cite above. What am I missing?
Thanks for any help.
Upvotes: 1
Views: 105
Reputation:
It wouldn't make sense to hold the lock while waiting. If you held the lock while waiting, you'd never be able to notify it.
According to §17.2.1 (Wait) of the JLS:
Thread t is added to the wait set of object m, and performs n unlock actions on m.
So, that means whilst the thread is waiting upon notification, the lock is released so notifiers can acquire the lock and drain the wait set.
Upvotes: 2
Reputation: 363
In the pattern
synchronized (msg) {
msg.wait();
}
the lock on msg
is indeed taken by the synchronized
statement. But the msg.wait()
call temporarily releases the lock and waits for a notification. A later msg.notify()
or msg.notifyAll()
can satisfy that wait. When the wait ends, the lock on msg
is taken again. In the example the Notifier thread does msg.notify()
, which means that one of the two Waiter threads can have its msg.wait()
satisfied. That doesn't happen immediately, because the msg.notify()
is inside a synchronized (msg)
block so the Notifier thread is holding the lock on msg
. As soon as that block exits, though, whichever thread's msg.wait()
got the notification will take back the lock on msg
and proceed.
This pattern is fairly fragile. The Notifier thread starts by doing a 5 second sleep, to make sure that the two Waiter threads have each reached their msg.wait()
. Without the sleep, it would be possible for Notifier to do msg.notify()
before either Waiter thread had done msg.wait()
, and the msg.notify()
would have had no effect. Because of issues like this, it is usually better to use synchronization classes such as Semaphore
from the java.util.concurrent
package, rather than using synchronized
and wait
/notify
.
Upvotes: 6