JUC知識點總結(五)生產者消費者模式的三種常見實現方法

11. 生產者\消費者模式

Object中的 wait()/notify()

要點:判斷條件時一定要用while()循環(虛假喚醒)

public class Shop{
    public int count = 0;
    public void produce(){
        count++;
        System.out.println(Thread.currentThread().getName()+" produce product , remain: "+count+"!");
    }

    public void sell(){
        count--;
        System.out.println(Thread.currentThread().getName()+" sell product , remain: "+count+"!");
    }

    public static void main(String[] args){
        Shop shop = new Shop();
        Factory f1 =shop.new Factory(shop);
        Factory f2 = shop.new Factory(shop);
        Consumer c1 =shop.new Consumer(shop);
        Consumer c2 = shop.new Consumer(shop);
        f1.start();
        f2.start();
        c1.start();
        c2.start();
    }

    class Factory extends Thread{
        private Shop shop;
        Factory(Shop shop){
            this.shop=shop;
        }
        @Override
        public void run(){
            while(true) {
                synchronized (shop) {
                    while (shop.count >= 10) {
                        try {
                            shop.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    shop.produce();
                    shop.notifyAll();
                }

            }
        }
    }

    class Consumer extends Thread{
        private Shop shop;
        Consumer(Shop shop){
            this.shop=shop;
        }
        @Override
        public void run(){
            while(true){
                synchronized (shop) {
                    while (shop.count <= 0) {
                        try {
                            shop.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    shop.sell();
                    shop.notifyAll();
                }
            }
        }
    }
}

ReentryLock,Condition實現

public class Shop{
    private int count = 0;
    private Lock lock = new ReentrantLock();
    private Condition empty = lock.newCondition();
    private Condition full = lock.newCondition();
    public void produce(){
        count++;
        System.out.println(Thread.currentThread().getName()+" produce product , remain: "+count+"!");
    }

    public void sell(){
        count--;
        System.out.println(Thread.currentThread().getName()+" sell product , remain: "+count+"!");
    }

    public static void main(String[] args){
        Shop shop = new Shop();
        Factory f1 =shop.new Factory(shop);
        Factory f2 = shop.new Factory(shop);
        Consumer c1 =shop.new Consumer(shop);
        Consumer c2 = shop.new Consumer(shop);
        f1.start();
        f2.start();
        c1.start();
        c2.start();
    }

    class Factory extends Thread{
        private Shop shop;
        Factory(Shop shop){
            this.shop=shop;
        }
        @Override
        public void run(){
            while(true) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                lock.lock();
                try {
                    while (shop.count >= 10) {
                        try {
                            full.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    shop.produce();
                    empty.signal();
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    class Consumer extends Thread{
        private Shop shop;
        Consumer(Shop shop){
            this.shop=shop;
        }
        @Override
        public void run(){
            while(true){
                try {
                    Thread.sleep(150);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                lock.lock();
                try {
                    while (shop.count <= 0) {
                        try {
                            empty.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    shop.sell();
                    full.signal();
                } finally {
                    lock.unlock();
                }
            }
        }
    }
}

阻塞隊列實現

BlockingQueue即阻塞隊列,從阻塞這個詞可以看出,在某些情況下對阻塞隊列的訪問可能會造成阻塞。被阻塞的情況主要有如下兩種:

  1. 當阻塞隊列爲空時,從阻塞隊列中取數據的操作會被阻塞。
  2. 當阻塞隊列爲滿時,往阻塞隊列中添加數據的操作會被阻塞。

從上可知,阻塞隊列是線程安全的。

JDK中的七大阻塞隊列

阻塞隊列名稱 說明
ArrayBlockingQueue 一個由數組結構組成的有界阻塞隊列。
LinkedBlockingQueue 一個由鏈表結構組成的有界阻塞隊列。
PriorityBlockingQueue 一個支持優先級排序的無界阻塞隊列。
DelayQueue 一個使用優先級隊列實現的無界阻塞隊列。
SynchronousQueue 一個不存儲元素的阻塞隊列。
LinkedTransferQueue 一個由鏈表結構組成的無界阻塞隊列。
LinkedBlockingDeque 一個由鏈表結構組成的雙向阻塞隊列。

ArrayBlockingQueue:
基於數組的阻塞隊列實現,其內部維護一個定長的數組,用於存儲隊列元素。線程阻塞的實現是通過ReentrantLock來完成的,數據的插入與取出共用同一個鎖,因此ArrayBlockingQueue並不能實現生產、消費同時進行。而且在創建ArrayBlockingQueue時,我們還可以控制對象的內部鎖是否採用公平鎖,默認採用非公平鎖。

LinkedBlockingQueue:
基於單向鏈表的阻塞隊列實現,在初始化LinkedBlockingQueue的時候可以指定對立的大小,也可以不指定,默認類似一個無限大小的容量(Integer.MAX_VALUE),不指隊列容量大小也是會有風險的,一旦數據生產速度大於消費速度,系統內存將有可能被消耗殆盡,因此要謹慎操作。另外LinkedBlockingQueue中用於阻塞生產者、消費者的鎖是兩個(鎖分離),因此生產與消費是可以同時進行的。

BlockingQueue接口的一些方法:

操作 拋異常 特定值 阻塞 超時
插入 add(o) offer(o) put(o) offer(o, timeout, timeunit)
移除 remove(o) poll(o) take(o) poll(timeout, timeunit)
檢查 element(o) peek(o)

這四類方法分別對應的是:
1 . ThrowsException:如果操作不能馬上進行,則拋出異常
2 . SpecialValue:如果操作不能馬上進行,將會返回一個特殊的值,一般是true或者false
3 . Blocks:如果操作不能馬上進行,操作會被阻塞
4 . TimesOut:如果操作不能馬上進行,操作會被阻塞指定的時間,如果指定時間沒執行,則返回一個特殊值,一般是true或者false

代碼實現:

public class Shop{
    private int count = 0;
    private BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);

    public void produce() {
        try {
            queue.put(1);
            count++;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+" produce product , remain: "+count+"!");
    }

    public void sell(){
        try {
            queue.take();
            count--;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+" sell product , remain: "+count+"!");
    }

    public static void main(String[] args){
        Shop shop = new Shop();
        Factory f1 =shop.new Factory(shop);
        Factory f2 = shop.new Factory(shop);
        Consumer c1 =shop.new Consumer(shop);
        Consumer c2 = shop.new Consumer(shop);
        f1.start();
        f2.start();
        c1.start();
        c2.start();
    }

    class Factory extends Thread{
        private Shop shop;
        Factory(Shop shop){
            this.shop=shop;
        }
        @Override
        public void run(){
            while(true) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                shop.produce();
            }
        }
    }

    class Consumer extends Thread{
        private Shop shop;
        Consumer(Shop shop){
            this.shop=shop;
        }
        @Override
        public void run(){
            while(true){
                shop.sell();
            }
        }
    }
}

下一篇
JUC知識點總結(六)多線程裏的HashMap與ConcurrentHashMap源碼分析

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