上一篇我們使用jdk原始的同步機制實現了生產者-消費者模式,這次我們改用jdk1.5中提供的相關併發類實現生產者-消費者模式。
先說明兩個類(接口)
- ReentrantLock類
- 作用跟synchronized代碼塊差不多,都是用於實現互斥,當然兩者是有區別的。
- Condition接口
- 它提供了await()、signal()、signalAll()等方法,作用於Object類的wait()、signal()和signalAll()方法一樣。注意由於它是一個接口,因此是不能new出來的,我們必須通過ReentrantLock類的newCondition方法得到一個Condition對象(實際上是ConditionObject類對象),這個對象與該ReentrantLock對象相關聯。
同樣,Condition的await()、signal()、signalAll()都必須在獲得相對應的ReentrantLock鎖才能進行調用
注意這裏我們可以使用同一個ReentrantLock對象多次調用newCondition獲得多個Condition對象,以實現更加複雜的同步關係,而使用Object類的相當於只能有一個Condition對象。當然,在不同的Condition對象中,等待和喚醒要相對應,比如說,我們兩次調用newCondition方法,得到了兩個Condition對象condition1和condition2,假如我在線程A調用condition1.await(),然後線程B調用了condition2.signal(),那麼線程A是一定不會因此被喚醒的,而應該調用condition1.signal()線程A纔可能會被喚醒,爲什麼說可能而不是一定呢?原因是signal()同Object類的notify()是一樣的,系統會隨機喚醒等待集中的一個線程,而我們的線程A不一定會被選到。
實現生產者-消費者模式(阻塞隊列):
public class ProducerConsumerCondition {
private int bufSize;
private int[] buf;
private int currentSize;
private ReentrantLock mainLock = new ReentrantLock();
private Condition putCondition = mainLock.newCondition();
private Condition getCondition = mainLock.newCondition();
public ProducerConsumerCondition() {
this(10);
}
public ProducerConsumerCondition(int bufSize) throws IllegalArgumentException{
this.bufSize = bufSize;
if(bufSize < 0) {
throw new IllegalArgumentException("bufSize can't less zero");
}
buf = new int[bufSize];
}
public void put(int v) throws InterruptedException{
mainLock.lock();
try {
while(currentSize >= bufSize) {
putCondition.await();
}
if(currentSize < bufSize) {
buf[currentSize++] = v;
getCondition.signal();
}
} catch(InterruptedException e) {
throw e;
} finally {
mainLock.unlock();
}
}
public int get() throws InterruptedException{
mainLock.lock();
try {
while(currentSize == 0) {
getCondition.await();
}
if(currentSize > 0) {
int temp = buf[currentSize - 1];
currentSize--;
putCondition.signal();
return temp;
}
} catch(InterruptedException e) {
throw e;
} finally {
mainLock.unlock();
}
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("[");
sb.append("queue's length=" + currentSize);
sb.append(" ");
for(int i = 0; i < currentSize - 1; i++) {
sb.append(buf[i] + ",");
}
if(currentSize > 0) {
sb.append(buf[currentSize - 1]);
}
sb.append("]");
return sb.toString();
}
}
說明
1.從代碼中可以看出使用ReentrantLock的一個缺點,就是每次lock完都必須要在finally塊中執行unLock,而這個我們不是每時每刻都會記得的。
轉載請註明原文地址:http://blog.csdn.net/u012619640/article/details/48090939
本博客已停止更新,轉移到微信公衆號上寫文章,歡迎關注:Android進階驛站