java 使用管程(monitor) 實現生產者-消費者模式

這是繼java 使用synchronized, wait(), notifyAll() 實現生產者-消費者模式的下一篇。

讓我們先來看看什麼是管程。管程的英文叫Monitor, 翻譯過來時監視器的意思。爲什麼要使用管程呢? 之前使用信號量實現了同步互斥,但是信號量有個一個缺點,那就是P操作和V操作很容易搞錯,如果一個地方進行了P操作,同步代碼塊執行完畢後忘記執行V操作,那麼很容易引起死鎖、飢餓等線程問題。這個問題類似C++裏面對象的創建和銷燬,如果沒有及時delete可能會產生內存泄露,如果過早銷燬則可能產生null pointer。 那管程其實就是爲了實現信號量的功能,但是不用讓編程人員來手動操作信號量。

來看看管程的模型:

 

從圖中可以看到,各個線程首先等待在entry queue中,然後一次只能有一個線程被開發進入管程區去執行操作(Operations)。 當該線程不滿足某個條件(比如條件X)時,該線程被掛在該條件變量的隊列中進行等待(wait),同時會喚醒(signal)一個正在等待的線程,並且要釋放當前拿到的鎖。

有了上面的原理。我們來看看如何實現一個消費者-生產者模式。

首先我們需要一個lock,來保證線程互斥地進入管程區,然後我們需要兩個條件變量notEmpty, notFull表示容器不爲空和容器不滿兩個條件,當這個兩個條件得不到滿足時,對應的執行消費和生產的線程分別掛在這兩個條件變量上。

public class Cache {
    private final static int SIZE = 100;
    private int count;
    private Lock lock;
    private Condition notFull;
    private Condition notEmpty;

    public Cache() {
        this.count = 0;
        this.lock = new ReentrantLock();
        this.notEmpty = lock.newCondition();
        this.notFull = lock.newCondition();
    }

    public void produce(){
        lock.lock();
        while(count == SIZE){
            try {
                System.out.println("容器已滿,生產者阻塞");
                notFull.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        count++;
        System.out.println("生產了一個,當前數量爲: "+ count);
        notEmpty.signal();
        lock.unlock();
    }

    public void consume(){
        lock.lock();
        while(count == 0){
            try {
                System.out.println("容器已空,消費者阻塞");
                notEmpty.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        count--;
        System.out.println("消費了一個,當前數量爲: "+ count);
        notFull.signal();
        lock.unlock();
    }
}
public class Consumer implements Runnable {
    private Cache cache;

    public Consumer(Cache cache){
        this.cache = cache;
    }

    @Override
    public void run() {
        while(true){
            try {
                cache.consume();
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}
public class Producer implements Runnable {
    private Cache cache;

    public Producer(Cache cache){
        this.cache = cache;
    }

    @Override
    public void run() {
        while(true){
            try {
                cache.produce();
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Main {
    public static void main(String[] args) {
        Cache cache = new Cache();

        Producer p = new Producer(cache);
        Consumer c = new Consumer(cache);

        int producerCount = 4, consumerCount = 2;
        for (int i = 0; i < producerCount; i++){
            new Thread(p).start();
        }
        for (int i = 0; i < consumerCount; i++){
            new Thread(c).start();
        }
    }
}

 

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