基於併發包同步機制實現生產者-消費者模式

上一篇我們使用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進階驛站
Android進階驛站

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章