1. 程式人生 > 其它 >java-阻塞佇列之LinkedBlockingQueue

java-阻塞佇列之LinkedBlockingQueue

概述

LinkedBlockingQueue內部由單鏈表實現,只能從head取元素,從tail新增元素。新增元素和獲取元素都有獨立的鎖,也就是說LinkedBlockingQueue是讀寫分離的,讀寫操作可以並行執行。LinkedBlockingQueue採用可重入鎖ReentrantLock來保證在併發情況下的執行緒安全。

構造器

LinkedBlockingQueue一共有三個構造器,分別是無參構造器、可以指定容量的構造器、可以穿入一個容器的構造器。如果在建立例項的時候呼叫的是無參構造器,LinkedBlockingQueue的預設容量是Integer.MAX_VALUE,這樣做很可能會導致佇列還沒有滿,但是記憶體卻已經滿了的情況(記憶體溢位)。

public LinkedBlockingQueue();   //設定容量為Integer.MAX
public LinkedBlockingQueue(int capacity);  //設定指定容量
public LinkedBlockingQueue(Collection<? extends E> c);  //穿入一個容器,如果呼叫該構造器,容量預設也是Integer.MAX_VALUE

LinkedBlockingQueue常用操作

取資料

take():首選。當佇列為空時阻塞

poll():彈出隊頂元素,佇列為空時,返回空

peek():和poll烈性,返回隊隊頂元素,但頂元素不彈出。佇列為空時返回null

remove(Object o):移除某個元素,佇列為空時丟擲異常。成功移除返回true

新增資料

put():首選。隊滿是阻塞

offer():隊滿時返回false

判斷佇列是否為空

size()方法會遍歷整個佇列,時間複雜度為O(n),所以最好選用isEmtpy

put元素原理

基本過程:

1.判斷元素是否為null,為null丟擲異常

2.加鎖(可中斷鎖)

3.判斷佇列長度是否到達容量,如果到達一直等待

4.如果沒有隊滿,enqueue()在隊尾加入元素

5.佇列長度加1,此時如果佇列還沒有滿,呼叫signal喚醒其他堵塞佇列

if (e == null) throw new NullPointerException();

        int c = -1;
        Node<E> node = new Node<E>(e);
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly();
        try {
            while (count.get() == capacity) {
                notFull.await();
            }
            enqueue(node);
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            putLock.unlock();
        }

take元素原理

基本過程:

1.加鎖(依舊是ReentrantLock),注意這裡的鎖和寫入是不同的兩把鎖

2.判斷佇列是否為空,如果為空就一直等待

3.通過dequeue方法取得資料

3.取走元素後佇列是否為空,如果不為空喚醒其他等待中的佇列

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;
    }

enqueue()和dequeue()方法實現都比較簡單,無非就是將元素新增到隊尾,從隊頂取走元素,感興趣的朋友可以自己去看一下,這裡就不貼上了。