问题
I know that similar questions have been discussed in this site, but I have not still got further by their aid considering a specific example. I can grasp the difference of notify() and notifyAll()
regarding Thread
"awakeining" in theory but I cannot perceive how they influence the functionality of program when either of them is used instead of the other. Therefore I set the following code and I would like to know what is the impact of using each one of them. I can say from the start that they give the same output (Sum is printed 3 times).
How do they differ virtually? How could someone modify the program, in order for the applying notify or notifyAll
to play a crucial role to its functionality (to give different results)?
Task:
class MyWidget implements Runnable {
private List<Integer> list;
private int sum;
public MyWidget(List<Integer> l) {
list = l;
}
public synchronized int getSum() {
return sum;
}
@Override
public void run() {
synchronized (this) {
int total = 0;
for (Integer i : list)
total += i;
sum = total;
notifyAll();
}
}
}
Thread:
public class MyClient extends Thread {
MyWidget mw;
public MyClient(MyWidget wid) {
mw = wid;
}
public void run() {
synchronized (mw) {
while (mw.getSum() == 0) {
try {
mw.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Sum calculated from Thread "
+ Thread.currentThread().getId() + " : " + mw.getSum());
}
}
public static void main(String[] args) {
Integer[] array = { 4, 6, 3, 8, 6 };
List<Integer> integers = Arrays.asList(array);
MyWidget wid = new MyWidget(integers);
Thread widThread = new Thread(wid);
Thread t1 = new MyClient(wid);
Thread t2 = new MyClient(wid);
Thread t3 = new MyClient(wid);
widThread.start();
t1.start();
t2.start();
t3.start();
}
}
UPDATE: I write it explicitly. The result is the same whether one uses notify or notifyAll: Sum calculated from Thread 12 : 27 Sum calculated from Thread 11 : 27 Sum calculated from Thread 10 : 27
Therefore my question: What is the difference?
回答1:
The difference is subtler than your example aims to provoke. In the words of Josh Bloch (Effective Java 2nd Ed, Item 69):
... there may be cause to use
notifyAll
in place ofnotify
. Just as placing thewait
invocation in a loop protects against accidental or malicious notifications on a publicly accessible object, usingnotifyAll
in place ofnotify
protects against accidental or maliciouswait
s by an unrelated thread. Suchwait
s could otherwise “swallow” a critical notification, leaving its intended recipient waiting indefinitely.
So the idea is that you must consider other pieces of code entering wait
on the same monitor you are waiting on, and those other threads swallowing the notification without reacting in the designed way.
Other pitfalls apply as well, which can result in thread starvation, such as that several threads may wait for different conditions, but notify
always happens to wake the same thread, and the one whose condition is not satisfied.
Even though not immediately related to your question, I feel it is important to quote this conclusion as well (emphasis by original author):
In summary, using
wait
andnotify
directly is like programming in “concurrency assembly language,” as compared to the higher-level language provided byjava.util.concurrent
. There is seldom, if ever, a reason to usewait
andnotify
in new code. If you maintain code that useswait
andnotify
, make sure that it always invokeswait
from within awhile
loop using the standard idiom. ThenotifyAll
method should generally be used in preference tonotify
. Ifnotify
is used, great care must be taken to ensure liveness.
回答2:
This is made clear in all sorts of docs. The difference is that notify()
selects (randomly) one thread, waiting for a given lock, and starts it. notifyAll()
instead, restarts all threads waiting for the lock.
Best practice suggests that threads always wait in a loop, exited only when the condition on which they are waiting is satisfied. If all threads do that, then you can always use notifyAll()
, guaranteeing that every thread whose wait condition has been satisfied, is restarted.
Edited to add hopefully enlightening code:
This program:
import java.util.concurrent.CountDownLatch;
public class NotifyExample {
static final int N_THREADS = 10;
static final char[] lock = new char[0];
static final CountDownLatch latch = new CountDownLatch(N_THREADS);
public static void main(String[] args) {
for (int i = 0; i < N_THREADS; i++) {
final int id = i;
new Thread() {
@Override public void run() {
synchronized (lock) {
System.out.println("waiting: " + id);
latch.countDown();
try { lock.wait(); }
catch (InterruptedException e) {
System.out.println("interrupted: " + id);
}
System.out.println("awake: " + id);
}
}
}.start();
}
try { latch.await(); }
catch (InterruptedException e) {
System.out.println("latch interrupted");
}
synchronized (lock) { lock.notify(); }
}
}
produced this output, in one example run:
waiting: 0
waiting: 4
waiting: 3
waiting: 6
waiting: 2
waiting: 1
waiting: 7
waiting: 5
waiting: 8
waiting: 9
awake: 0
None of the other 9 threads will ever awaken, unless there are further calls to notify.
回答3:
notify
wakes (any) one thread in the wait set, notifyAll
wakes all threads in the waiting set. notifyAll
should be used most of the time. If you are not sure which to use, then use notifyAll
.
In some cases, all waiting threads can take useful action once the wait finishes. An example would be a set of threads waiting for a certain task to finish; once the task has finished, all waiting threads can continue with their business. In such a case you would use notifyAll()
to wake up all waiting threads at the same time.
Another case, for example mutually exclusive locking, only one of the waiting threads can do something useful after being notified (in this case acquire the lock). In such a case, you would rather use notify()
. Properly implemented, you could use notifyAll()
in this situation as well, but you would unnecessarily wake threads that can't do anything anyway.
Javadocs on notify.
Javadocs on notifyAll.
回答4:
Once only one thread is waiting to sum to not be zero, there is no difference. If there are several threads waiting, notify will wake up only one of them, and all the other will wait forever.
Run this test to better understand the difference:
public class NotifyTest implements Runnable {
@Override
public void run ()
{
synchronized (NotifyTest.class)
{
System.out.println ("Waiting: " + this);
try
{
NotifyTest.class.wait ();
}
catch (InterruptedException ex)
{
return;
}
System.out.println ("Notified: " + this);
}
}
public static void main (String [] args) throws Exception
{
for (int i = 0; i < 10; i++)
new Thread (new NotifyTest ()).start ();
Thread.sleep (1000L); // Let them go into wait ()
System.out.println ("Doing notify ()");
synchronized (NotifyTest.class)
{
NotifyTest.class.notify ();
}
Thread.sleep (1000L); // Let them print their messages
System.out.println ("Doing notifyAll ()");
synchronized (NotifyTest.class)
{
NotifyTest.class.notifyAll ();
}
}
}
回答5:
I found what is going on with my program. The three Thread
s print the result even with the notify()
, because they do not manage to enter the waiting state. The calculation in the widThread
is performed quickly enough to preempt the entering of the other Thread
s in the waiting state, since it depends on the condition mw.getSum() == 0
(while loop). The widThread
calculates the sum, so that the remaining Thread
s do not ever "see" its value as 0.
If the while loop is removed and the start of widThread
comes after the start of the other Thread
s, then by notify()
only one Thread
prints the result and the others are waiting forever, as the theory and the other answers indicate.
来源:https://stackoverflow.com/questions/14924610/difference-between-notify-and-notifyall