我不理解Java Concurrency in Practice书中的以下片段。
当只有一个线程可以取得进展时,使用notifyAll是低效的--有时是一点,有时是严重的。如果有十个线程在条件队列上等待,调用notifyAll会导致它们每个线程都被唤醒,并争夺锁;然后它们中的大多数或所有线程会马上回到睡眠状态。这意味着每个事件都要进行大量的上下文切换和大量的争夺锁的获取,使(也许)一个线程取得进展。在最坏的情况下,使用notifyAll的结果是O(n2)的唤醒,其中n就足够了。)
一个例子在清单14.6中。
@ThreadSafe
public class BoundedBuffer<V> extends BaseBoundedBuffer<V> {
// CONDITION PREDICATE: not-full (!isFull())
// CONDITION PREDICATE: not-empty (!isEmpty())
public BoundedBuffer(int size) { super(size); }
// BLOCKS-UNTIL: not-full
public synchronized void put(V v) throws InterruptedException {
while (isFull())
wait();
doPut(v);
notifyAll();
}
// BLOCKS-UNTIL: not-empty
public synchronized V take() throws InterruptedException {
while (isEmpty())
wait();
V v = doTake();
notifyAll();
return v;
}
}
例如,我们可以有以下事件序列。
现在只有一个线程可以取得进展--消费者线程。我们还有100000个生产者,他们不能取得进展。
我不明白为什么在最坏的情况下,会有O(n)2)唤醒,在能取得进展的线程被唤醒之前。
我认为最坏的情况是以下顺序
我想我们 "使用 "了O(n)+O(n)*O(1)=O(n)次唤醒。
是书上有错误,还是我这里有遗漏?
东西被放入队列n次。"n次唤醒就够了 "的意思是,理想情况下,我们希望当一个生产者把东西放进缓冲区的时候,比如说,一个消费者会被通知到,这样就会有n次通知,甚至更好的是,这些通知都是无争议的。但是相反,所有等待锁的线程,包括所有的生产者(减去1,做投放的那个)和所有的消费者(反正是在等待的那个),每次有东西掉进队列里都会收到通知,他们都会为了锁而争夺,调度器会选出一个赢家。我们甚至没有考虑被选中的线程要等待的情况,这只是一个细节)。所以有n次notifyAll被调用,每次投放一次,每次notifyAll都会唤醒多个生产者和多个消费者,这就是他们得到O(n)2)唤醒。
假设我们有n个消费者线程和n个生产者线程,并且缓冲区是空的(满缓冲区的例子类似)。所有线程都处于准备运行状态(调度器可以选择任何线程运行)。
如果有消费者线程运行 - 它将进入等待状态。如果有生产者运行 - 它将成功并调用notifyAll()。
最大化wait()调用(和唤醒)数量的情况。
Example for 5 producer and 5 consumer
+--------------+-------------------------------------+
| C-C-C-C-C-P | all consumers move to waiting state |
+--------------+-------------------------------------+
| C*-C-C-C-C-P | 5 wake ups |
+--------------+-------------------------------------+
| C*-C-C-C-P | 4 wake ups |
+--------------+-------------------------------------+
| C*-C-C-P | 3 wake ups |
+--------------+-------------------------------------+
| C*-C-P | 2 wake ups |
+--------------+-------------------------------------+
| C* | 1 wake up |
+--------------+-------------------------------------+
P - producer
C - consumer
C* - consumer that succesfully finish take() method ( without wait() invoking)
让我们来数数:5 + 4 + 3 + 2 + 1 = 15.
对于n个生产者和n个消费者:n+(n-1)+(n-2)+(n-3)+...+1=1+2+3+4+...+n=前n个元素之和。等差数列 =n * (1 + n) 2 = (n + n^2) 2 → O(n^2)