java消費者生產者模式及JDK之阻塞隊列LinkedBlockingQueue實現

來源:互聯網
上載者:User

標籤:linkedblockingqueue   生產者   消費者   

生產者消費者問題

   (英語:Producer-consumer problem),也稱有限緩衝問題(英語:Bounded-buffer problem),是一個多線程同步問題的經典案例。該問題描述了兩個共用固定大小緩衝區的線程——即所謂的“生產者”和“消費者”——在實際運行時會發生的問題。生產者的主要作用是產生一定量的資料放到緩衝區中,然後重複此過程。與此同時,消費者也在緩衝區消耗這些資料。該問題的關鍵就是要保證生產者不會在緩衝區滿時加入資料,消費者也不會在緩衝區中空時消耗資料。

    要解決該問題,就必須讓生產者在緩衝區滿時休眠(要麼乾脆就放棄資料),等到下次消費者消耗緩衝區中的資料的時候,生產者才能被喚醒,開始往緩衝區添加資料。同樣,也可以讓消費者在緩衝區空時進入休眠,等到生產者往緩衝區添加資料之後,再喚醒消費者。通常採用處理序間通訊的方法解決該問題,常用的方法有號誌法[1]等。如果解決方案不夠完善,則容易出現死結的情況。出現死結時,兩個線程都會陷入休眠,等待對方喚醒自己。該問題也能被推廣到多個生產者和消費者的情形。

現實中的應用

     比如一個飯店,它有一個廚師和一個服務員。這個服務員必須等待廚師準備好食物。當廚師準備好時,他會通知服務員,之後服務員上菜,然後返回繼續等待。這是一個任務協作的執行個體:廚師代表生產者,服務員代表消費者。兩個任務必須在食物被生產和消費時進行握手,而系統必須以有序的方式關閉。

      可以用來表示這種關係。

     


生產者消費者的實現

這兒是用阻塞隊列LinkedBlockingQueue來實現的,阻塞隊列

package com.a.consumer;import java.util.concurrent.*;public class consumer3 {    // 建立一個阻塞隊列    private LinkedBlockingQueue<Object> queue = new LinkedBlockingQueue<Object>(10);    public consumer3() {    }    public void start() {        new Producer().start();        new Consumer().start();    }    public static void main(String[] args) throws Exception {    consumer3 s3 = new consumer3();        s3.start();    }    class Producer extends Thread {        public void run() {            while (true) {                try {                    Object o = new Object();                    // 取出一個對象                    queue.put(o); //隊列滿時會自動阻塞                    System.out.println("Producer: " + o);                } catch (InterruptedException e) {                e.printStackTrace();                }            }        }    }    class Consumer extends Thread {        public void run() {            while (true) {                try {                    // 取出一個對象                    Object o = queue.take();                    System.out.println("Consumer: " + o);                } catch (InterruptedException e) {                e.printStackTrace();                }            }        }    }}
下面研究下LinkedBlockingQueue的源碼

首先看一下它的put方法

注意下面這句話,它會調用putLock.lockInterruptibly()這個方法,來試圖擷取這個putLock這個鎖

    public void put(E e) throws InterruptedException {        if (e == null) throw new NullPointerException();        // Note: convention in all put/take/etc is to preset local var        // holding count negative to indicate failure unless set.        int c = -1;        final ReentrantLock putLock = this.putLock;        final AtomicInteger count = this.count;        putLock.lockInterruptibly();          try {            /*             * Note that count is used in wait guard even though it is             * not protected by lock. This works because count can             * only decrease at this point (all other puts are shut             * out by lock), and we (or some other waiting put) are             * signalled if it ever changes from             * capacity. Similarly for all other uses of count in             * other wait guards.             */            while (count.get() == capacity) {                     notFull.await();            }            enqueue(e);            c = count.getAndIncrement();            if (c + 1 < capacity)                notFull.signal();        } finally {            putLock.unlock();        }        if (c == 0)            signalNotEmpty();    }

在看lockInterruptibly()方法的源碼,實際是調用的sync這個同步器的acquireInterruptibly這個方法

    public void lockInterruptibly() throws InterruptedException {        sync.acquireInterruptibly(1);    }

在看acquireInterruptibly這個方法,就是先檢查當前線程中斷標識位是不是true,是true時,將拋出中斷異常,否則會試著去擷取鎖,當沒有獲得鎖時,會執行doAcquireInterruptibly這個方法

    public final void acquireInterruptibly(int arg) throws InterruptedException {        if (Thread.interrupted())            throw new InterruptedException();        if (!tryAcquire(arg))            doAcquireInterruptibly(arg);    }
下面在看一下doAcquireInterruptibly這個方法,要注意shouldParkAfterFailedAcquire這個方法,就是當它為true時,會接著執行parkAndCheckInterrupt()這個方法,當它也為真時,會跳出當前迴圈,然後取消擷取鎖,並且同時拋出異常。

    private void doAcquireInterruptibly(int arg)        throws InterruptedException {        final Node node = addWaiter(Node.EXCLUSIVE);        try {            for (;;) {                final Node p = node.predecessor();                if (p == head && tryAcquire(arg)) {                    setHead(node);                    p.next = null; // help GC                    return;                }                if (shouldParkAfterFailedAcquire(p, node) &&                    parkAndCheckInterrupt())                    break;            }        } catch (RuntimeException ex) {            cancelAcquire(node);            throw ex;        }        // Arrive here only if interrupted        cancelAcquire(node);        throw new InterruptedException();    }




java消費者生產者模式及JDK之阻塞隊列LinkedBlockingQueue實現

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.