天天看點

java 線程 condition_java多線程學習(六) 之 Condition

和synchronized一樣,Lock是為了線程互斥而設計的,在synchronized中,使用對象隐式鎖的wait notify(notifyAll)來實作線程之間的通信,同樣的功能在Lock中是通過Codition來實作的。Condition除了完成wait和notify的基本功能之外,它的好處在于一個Lock可以通過建立多個Condition,選擇性的去通知wait的線程。

官方解釋:

Condition factors out the Object monitor methods (wait, notify and notifyAll) into distinct objects to give the effect of having multiple wait-sets per object, by combining them with the use of arbitrary Lock implementations. Where a Lock replaces the use of synchronized methods and statements, a Condition replaces the use of the Object monitor methods.

Condition把隐式鎖的wait notify notifyAll功能提取出來,賦予确切的對象,讓一個對象有個多等待集(wait-sets),這些condition也是和Lock執行個體綁定的,換句話說,Lock對象代替了synchronized,condition代替了wait、notify(notifyAll)等方法。

因為一個Lock可以生成多個Condition,是以condition可以讓所有的因Lock等待的線程分成幾個互相等待的子集合,也就是前面提到的wait-sets.

Conditions (also known as condition queues or condition variables) provide a means for one thread to suspend execution (to “wait”) until notified by another thread that some state condition may now be true. Because access to this shared state information occurs in different threads, it must be protected, so a lock of some form is associated with the condition. The key property that waiting for a condition provides is that it atomically releases the associated lock and suspends the current thread, just like Object.wait.

Condtions(也可以叫condition隊列,或者是condition變量),它提供一個方法,讓一個線程在其它活着(condition狀态為true)的線程在通知它之前,一直處于等待狀态。因為不同的線程通路共享資源,必須是在擷取鎖的情況下。是以Condition的使用要在Lock内。這就是condition的一個重要特性,原子的釋放鎖,并且挂起線程。和Object.wait效果一樣。

官方示例:

官方示例的場景是一個關于生産者消費者的模式。有多個生産者和消費者。但是隻有一個倉庫。

首先我們要保證在同一時間隻有一個人在生産或者消費。

其次我們希望當一個消費者在消費完最後一個物品的時候,去通知任意一個生産者來生産,同樣,我們希望當一個生産者把倉庫填滿的時候,通知任意一個在等待的消費者來消費,而不會通知其它生産者繼續生産。

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(); //鎖1

try {

while (count == items.length)

notFull.await();//鎖2

items[putptr] = x;

if (++putptr == items.length) putptr = 0;

++count; //生産

notEmpty.signal(); //通知1

} finally {

lock.unlock();

}

}

public Object take() throws InterruptedException {

lock.lock();//鎖3

try {

while (count == 0)

notEmpty.await();//鎖4

Object x = items[takeptr];

if (++takeptr == items.length) takeptr = 0;

--count;

notFull.signal();//通知2

return x;//消耗

} finally {

lock.unlock();

}

}

}

官方示例,即展示了Condition實作了隐式鎖的wait notify notifyAll的功能,也包含了condition的不同點。

假設有多個線程在分别調用take方法和put方法。假設所有的線程都挂在了鎖1、鎖2、鎖3、鎖4的地方。

隻有一個線程到達了通知1的地方, 它隻會通知到鎖1、鎖3 和因同一condition而等待的鎖4的地方,繼續往下執行。而不會通知鎖2。

這樣就保證了在items在生産之後隻會去通知等待消耗線程去消耗。同樣的,在items消耗光之後,隻會去通知生産線程去生産, Lock也同時保證了生産消耗不能同時進行。

如果我們使用Object.wait和Object.notify來實作上面的方案。item空了的時候,假設恰巧每次喚醒的都是消費者線程,這個生産消費的過程就無法繼續下去了。