如果一个人在谷歌上搜索“notify()和notifyAll()之间的区别”,那么会跳出很多解释(撇开javadoc段落)。这都归结于被唤醒的等待线程的数量:notify()中有一个,notifyAll()中有所有线程。
然而(如果我确实理解了这些方法之间的区别),只有一个线程总是被选择用于进一步的监视采集;第一种情况是VM选择的线程,第二种情况是系统线程调度程序选择的线程。程序员不知道它们的确切选择过程(在一般情况下)。
那么notify()和notifyAll()之间有什么有用的区别呢?我遗漏了什么吗?
我认为这取决于资源是如何生产和消费的。如果同时有5个工作对象,并且您有5个消费者对象,那么使用notifyAll()唤醒所有线程是有意义的,这样每个线程都可以处理1个工作对象。
如果您只有一个可用的工作对象,那么唤醒所有使用者对象来争夺这个对象有什么意义呢?第一个检查可用工作的线程将得到它,所有其他线程将检查并发现它们无事可做。
我在这里找到了一个很好的解释。简而言之:
The notify() method is generally used
for resource pools, where there
are an arbitrary number of "consumers"
or "workers" that take resources, but
when a resource is added to the pool,
only one of the waiting consumers or
workers can deal with it. The
notifyAll() method is actually used in
most other cases. Strictly, it is
required to notify waiters of a
condition that could allow multiple
waiters to proceed. But this is often
difficult to know. So as a general
rule, if you have no particular
logic for using notify(), then you
should probably use notifyAll(),
because it is often difficult to know
exactly what threads will be waiting
on a particular object and why.
我想提一下《Java并发实践》中解释的内容:
第一点,是Notify还是NotifyAll?
It will be NotifyAll, and reason is that it will save from signall hijacking.
If two threads A and B are waiting on different condition predicates
of same condition queue and notify is called, then it is upto JVM to
which thread JVM will notify.
Now if notify was meant for thread A and JVM notified thread B, then
thread B will wake up and see that this notification is not useful so
it will wait again. And Thread A will never come to know about this
missed signal and someone hijacked it's notification.
So, calling notifyAll will resolve this issue, but again it will have
performance impact as it will notify all threads and all threads will
compete for same lock and it will involve context switch and hence
load on CPU. But we should care about performance only if it is
behaving correctly, if it's behavior itself is not correct then
performance is of no use.
这个问题可以通过使用jdk 5中提供的显式锁定Lock的Condition对象来解决,因为它为每个条件谓词提供了不同的等待。在这里,它将表现正确,不会有性能问题,因为它将调用信号,并确保只有一个线程正在等待该条件
这里有一个例子。运行它。然后将notifyAll()中的一个更改为notify(),看看会发生什么。
ProducerConsumerExample类
public class ProducerConsumerExample {
private static boolean Even = true;
private static boolean Odd = false;
public static void main(String[] args) {
Dropbox dropbox = new Dropbox();
(new Thread(new Consumer(Even, dropbox))).start();
(new Thread(new Consumer(Odd, dropbox))).start();
(new Thread(new Producer(dropbox))).start();
}
}
Dropbox类
public class Dropbox {
private int number;
private boolean empty = true;
private boolean evenNumber = false;
public synchronized int take(final boolean even) {
while (empty || evenNumber != even) {
try {
System.out.format("%s is waiting ... %n", even ? "Even" : "Odd");
wait();
} catch (InterruptedException e) { }
}
System.out.format("%s took %d.%n", even ? "Even" : "Odd", number);
empty = true;
notifyAll();
return number;
}
public synchronized void put(int number) {
while (!empty) {
try {
System.out.println("Producer is waiting ...");
wait();
} catch (InterruptedException e) { }
}
this.number = number;
evenNumber = number % 2 == 0;
System.out.format("Producer put %d.%n", number);
empty = false;
notifyAll();
}
}
消费阶层
import java.util.Random;
public class Consumer implements Runnable {
private final Dropbox dropbox;
private final boolean even;
public Consumer(boolean even, Dropbox dropbox) {
this.even = even;
this.dropbox = dropbox;
}
public void run() {
Random random = new Random();
while (true) {
dropbox.take(even);
try {
Thread.sleep(random.nextInt(100));
} catch (InterruptedException e) { }
}
}
}
生产类
import java.util.Random;
public class Producer implements Runnable {
private Dropbox dropbox;
public Producer(Dropbox dropbox) {
this.dropbox = dropbox;
}
public void run() {
Random random = new Random();
while (true) {
int number = random.nextInt(10);
try {
Thread.sleep(random.nextInt(100));
dropbox.put(number);
} catch (InterruptedException e) { }
}
}
}