日期:2014-05-20  浏览次数:20698 次

Condition 问题 很不能理解为什么要用2个Condition


 class BoundedBuffer {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition(); 
   final Condition notEmpty = lock.newCondition(); 

   final Object[] items = new Object[100];
   int putptr, takeptr, count;

   public void put(Object x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length) 
         notFull.await();
       items[putptr] = x; 
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal();
     } finally {
       lock.unlock();
     }
   }

   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0) 
         notEmpty.await();
       Object x = items[takeptr]; 
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal();
       return x;
     } finally {
       lock.unlock();
     }
   } 
 }



Condition 问题  很不能理解为什么要用2个Condition
------最佳解决方案--------------------
当然可以使用一个Condition,但要注意,使用一个Condition的时候就要使用signalAll方法了,这换唤醒所有等待的线程,而最终可能只有一个线程的条件满足了,比如buffer现在是空的,有100个线程在等待,现在加入一个元素,唤醒了100个线程,线程从wait中醒来需要重新获取锁,这100个线程就要竞争同一把锁,而最终只有一个线程能消费这个元素,其它99个线程醒来后又继续while (count == 0)成立,继续await,在高并发下,这是非常大的竞争与上下文切换开销

为什么一个Condition就必须用notifyAll?因为这个Condition是作为两个条件断言使用的。
假设有buffer的长度是10,现在10个元素都被消费完了,且还有15个线程在上面等待
现在有10个线程各往里put一个元素,虽然每put一次都会做唤醒操作,但是存在唤醒的线程一直竞争不到锁的情况,于是那15个线程还在等待,而10个线程很幸运,在与那15个线程的竞争过程中都先获取到锁了,且把元素放进去了,这是bufer里就有10个元素了,也就是满了,put的线程又来了,这次又来了10个,他们都需要wait,现在是什么场景?有10个元素,15个线程在等待获取锁然后拿到元素,10个线程等待往buffer里塞东西,如果只有一个Condition,且使用的是signal而不是signalAll,在消费了一个元素后,本来该唤醒一个生产线程,而它却有可能唤醒的还是消费线程,因为消费线程和生产线程都在同一个条件队列里等待,这就出问题了。

如果LZ读过《Java concurrency in practice》第14章,将会看到很多BoundedBuffer的实现

http://www.ticmy.com/?p=219
------其他解决方案--------------------
所谓condition就是指条件嘛,一般是用来阻塞线程的,或者说满足某个条件才能让线程继续往下运行。
这里一个是表示满的状态,一个表示空的状态。

------其他解决方案--------------------
引用:
所谓condition就是指条件嘛,一般是用来阻塞线程的,或者说满足某个条件才能让线程继续往下运行。
这里一个是表示满的状态,一个表示空的状态。


如果用一个不可以吗? 为什么不可以?