Java並發學習筆記(八)-LinkedBlockingQueue

來源:互聯網
上載者:User

標籤:阻塞隊列   linkedblockingqueue   並發   blockingqueue   

LinkedBlockingQueue是由鏈表組成的阻塞隊列,先來看demo

public class LinkedBlockingQueueDemo {public static void main(String[] args) {ExecutorService es = Executors.newCachedThreadPool();BlockingQueue<Bread> queue = new LinkedBlockingQueue<Bread>(10);for(int i = 0; i < 2; i++){es.execute(new Baker(queue));}for(int i = 0; i < 10; i++){es.execute(new BreadConsumer(queue));}es.shutdown();}}class Baker implements Runnable{private static int no = 0;private int id = ++no;private int count = 0;private BlockingQueue<Bread> queue;public Baker(BlockingQueue<Bread> queue){this.queue = queue;}@Overridepublic void run() {for(int i = 0; i < 10; i++){System.out.println("麵包師" + id + "正準備做第" + ++count + "麵包");Bread bread = new Bread();//滿隊列情況下,阻塞try {queue.put(bread);System.out.println("麵包師" + id + "做的第" + count + "麵包是麵包"+ bread.getId());} catch (InterruptedException e) {}}}}class BreadConsumer implements Runnable{private static int no = 0;private int id = ++no;private int count = 0;private BlockingQueue<Bread> queue;public BreadConsumer(BlockingQueue<Bread> queue){this.queue = queue;}@Overridepublic void run() {for(int i = 0; i < 2; i++){System.out.println("顧客 " + id + "準備買第" + ++count +"個麵包" );Bread bread = null;//空隊列情況下,阻塞try {bread = queue.take();} catch (InterruptedException e) {}<span style="white-space:pre"></span>System.out.println("顧客" + id + "買到的第" +count+"個麵包是麵包" + bread.getId());}}}class Bread {private static int count = 0;private int id = ++count;public int getId() {return id;}}

預設情況下,其容量為Integer.MAX_VALUE

    public LinkedBlockingQueue() {        this(Integer.MAX_VALUE);    }
也可以指定一個容量以免鏈表過度擴張

    public LinkedBlockingQueue(int capacity) {        if (capacity <= 0) throw new IllegalArgumentException();        this.capacity = capacity;        last = head = new Node<E>(null);    }
在LinkedBlockingQueue的鏈表裡,有兩個指標,分別指向隊列頭和隊列尾。與ArrayBlockingQueue不同的是,在LinkedBlockingQueue裡,有兩把鎖,分別鎖隊列頭和隊列尾。這樣做是有好處的,可以同時入隊和出隊,比ArrayBlockingQueue效能高
    /** 鏈表頭 */    private transient Node<E> head;    /** 鏈表尾 */    private transient Node<E> last;    /** 出隊的鎖 */    private final ReentrantLock takeLock = new ReentrantLock();    /** Wait queue for waiting takes */    private final Condition notEmpty = takeLock.newCondition();    /** 入隊的鎖 */    private final ReentrantLock putLock = new ReentrantLock();    /** Wait queue for waiting puts */    private final Condition notFull = putLock.newCondition();
我們來看put阻塞方法,如果隊列滿,會一直阻塞,直到隊列不滿

    public void put(E e) throws InterruptedException {        if (e == null) throw new NullPointerException();        int c = -1;        final ReentrantLock putLock = this.putLock;        final AtomicInteger count = this.count;        putLock.lockInterruptibly();        try {            while (count.get() == capacity) {                     notFull.await();            }            enqueue(e);            c = count.getAndIncrement();            if (c + 1 < capacity)                notFull.signal();        } finally {            putLock.unlock();        }        if (c == 0)            signalNotEmpty();    }
往隊列裡插入資料時,首先會持有putLock鎖,如果當前隊列元素個數跟容量相等,阻塞,調用notFull.await。不然,入隊。入隊後如果元素個數小於隊列容量,會喚醒其它的阻塞的插入線程。最後一句,不明白,元素為0,為什麼會去執行喚醒空條件?求指教

隊列裡,插入元素,會插入隊尾

    private void enqueue(E x) {        // assert putLock.isHeldByCurrentThread();        last = last.next = new Node<E>(x);    }
再來看出隊操作take,也是阻塞方法

    public E take() throws InterruptedException {        E x;        int c = -1;        final AtomicInteger count = this.count;        final ReentrantLock takeLock = this.takeLock;        takeLock.lockInterruptibly();        try {                while (count.get() == 0) {                    notEmpty.await();                }            x = dequeue();            c = count.getAndDecrement();            if (c > 1)                notEmpty.signal();        } finally {            takeLock.unlock();        }        if (c == capacity)            signalNotFull();        return x;    }
出隊擷取的是takeLock,類似的,如果當前隊列元素個數為0,阻塞,調用notEmpty.await。不然,出隊。出隊後如果元素個數大於0,會喚醒其它的阻塞的出隊線程。出隊從隊頭出隊

    private E dequeue() {        // assert takeLock.isHeldByCurrentThread();        Node<E> h = head;        Node<E> first = h.next;        h.next = h; // help GC        head = first;        E x = first.item;        first.item = null;        return x;    }

Java並發學習筆記(八)-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.