2016-08-23 14 views
2

收購這可能是一個愚蠢的問題,但我不明白爲什麼一個單一的ReentrantLock可以通過兩個不同的線程在這個類採取(這是我使用ArrayBlockingQueue的簡化解決方案與線程和鎖)玩:爲什麼用條件的ReentrantLock可以通過兩個線程在ArrayBlockingQueue

class SampleThreadSafeQueue { 

     private ReentrantLock lock = new ReentrantLock(); 
     private List<Integer> list = new ArrayList<>(); 
     private Condition notEmpty; 
     private Condition notFull; 
     private volatile int count; 

     public SampleThreadSafeQueue() { 
      notEmpty = lock.newCondition(); 
      notFull = lock.newCondition(); 
     } 

     public int take() { 
      try { 
       lock.lock(); 
       System.out.println(Thread.currentThread().getName() +": acquired lock in take()"); 
       while (count == 0) { 
        notEmpty.await(); 
       } 

       return extract(); 
      } catch (Exception e) { 
       e.printStackTrace(); 
       return 0; 
      } finally { 
       System.out.println(Thread.currentThread().getName() +": released lock for take()"); 
       lock.unlock(); 
      } 
     } 

     private int extract() { 
      int index = count <= 0 ? 0 : count - 1; 
      Integer integer = list.get(index); 
      list.remove(index); 
      count--; 
      list.clear(); 
      notFull.signal(); 
      return integer; 
     } 

     public void put(int value) { 
      try { 
       lock.lock(); 
       System.out.println(Thread.currentThread().getName() +": acquired lock in put()"); 
       while (!list.isEmpty()) { 
        notFull.await(); 
       } 
       Thread.sleep(3000); // let's assume it takes 3 secs to add value 
       insert(value); 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } finally { 
       System.out.println(Thread.currentThread().getName() +": released lock for put()"); 
       lock.unlock(); 
      } 
     } 


     private void insert(int value) { 
      list.add(value); 
      count = list.size(); 
      notEmpty.signal(); 
     } 
    } 

我接受這個結果在控制檯:

pool-1-thread-1: acquired lock in put() 
pool-1-thread-1: released lock for put() 
pool-1-thread-1: acquired lock in put() - this guy has taken the lock 
pool-1-thread-2: acquired lock in take() - that guy has taken the lock as well! wtf? 
pool-1-thread-2: released lock for take() 
Value = 0 
pool-1-thread-2: acquired lock in take() 
pool-1-thread-1: released lock for put() 
pool-1-thread-1: acquired lock in put() 
pool-1-thread-2: released lock for take() 
Value = 1 
pool-1-thread-1: released lock for put() 
pool-1-thread-1: acquired lock in put() 
pool-1-thread-2: acquired lock in take() 
pool-1-thread-2: released lock for take() 
Value = 2 
... 

我懷疑這是因爲條件的我用while循環,但在邏輯上無法理解爲什麼它會發生。 欣賞你的解釋。謝謝!

回答

1

當你調用對從一些鎖獲得Conditionawait(),你的線程釋放鎖,並停止,直到Condition從另一個線程通知(通過signal()signalAll()手段)。

因此,您的線程1獲取鎖定,但隨後調用await()並切換到等待模式,釋放最近被線程2獲取的鎖定。線程2完成後,它會通知線程1(通過調用signal())並釋放鎖定。鎖定立即被線程1重新獲得,它喚醒並繼續做其工作,並最終釋放鎖。

然後他們重複它在一個順序稍有不同。

1

看看條件文檔http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Condition.html

你要明白那是什麼條件(AWAIT,信號)像同步(等待,通知)。撥打await()將釋放鎖上的保留。 (你仍然需要撥打lock.unlock()壽)。 put()等待take()功能觸發notFull.signal()take()等待put()觸發notEmpty.signal()。該鎖落得像你是用來直接在await()來電和未lock.lock()同步。

這是記錄例如:

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(); 
    } 
    } 
} 
相關問題