java.util.concurrent系列之--LinkedBlockingQueue

1、LinkedBlockingQueue介紹

LinkedBlockingQueue是一個單向鏈表實現的阻塞隊列。該隊列按 FIFO(先進先出)排序元素,新元素插html

入到隊列的尾部,而且隊列獲取操做會得到位於隊列頭部的元素。連接隊列的吞吐量一般要高於基於數組的java

隊列,可是在大多數併發應用程序中,其可預知的性能要低。數組

此外,LinkedBlockingQueue仍是可選容量的(防止過分膨脹),便可以指定隊列的容量。若是不指定,默認安全

容量大小等於Integer.MAX_VALUE。數據結構

2、LinkedBlockingQueue原理和數據結構

LinkedBlockingQueue的數據結構,以下圖所示:多線程

輸入圖片說明

說明:併發

1. LinkedBlockingQueue繼承於AbstractQueue,它本質上是一個FIFO(先進先出)的隊列。

2. LinkedBlockingQueue實現了BlockingQueue接口,它支持多線程併發。當多線程競爭同一個資源

時,某線程獲取到該資源以後,其它線程須要阻塞等待。ide

3. LinkedBlockingQueue是經過單鏈表實現的。

(01) head是鏈表的表頭。取出數據時,都是從表頭head處插入。

(02) last是鏈表的表尾。新增數據時,都是從表尾last處插入。

(03) count是鏈表的實際大小,即當前鏈表中包含的節點個數。

(04) capacity是列表的容量,它是在建立鏈表時指定的。

(05) putLock是插入鎖,takeLock是取出鎖;notEmpty是「非空條件」,notFull是「未滿條件」。經過它們

對鏈表進行併發控制。函數

LinkedBlockingQueue在實現「多線程對競爭資源的互斥訪問」時,對於「插入」和「取出(刪除)」操做分別使用了性能

不一樣的鎖。對於插入操做,經過「插入鎖putLock」進行同步;對於取出操做,經過「取出鎖takeLock」進行同

步。

此外,插入鎖putLock和「非滿條件notFull」相關聯,取出鎖takeLock和「非空條件notEmpty」相關聯。通

過notFull和notEmpty更細膩的控制鎖。

4. 注

若某線程(線程A)要取出數據時,隊列正好爲空,則該線程會執行notEmpty.await()進行等待;當其它某個線

程(線程B)向隊列中插入了數據以後,會調用notEmpty.signal()喚醒「notEmpty上的等待線程」。此時,線程A

會被喚醒從而得以繼續運行。 此外,線程A在執行取操做前,會獲取takeLock,在取操做執行完畢再釋放

takeLock。

若某線程(線程H)要插入數據時,隊列已滿,則該線程會它執行notFull.await()進行等待;當其它某個線程(線

程I)取出數據以後,會調用notFull.signal()喚醒「notFull上的等待線程」。此時,線程H就會被喚醒從而得以繼

續運行。 此外,線程H在執行插入操做前,會獲取putLock,在插入操做執行完畢才釋放putLock。

3、LinkedBlockingQueue函數列表

// 建立一個容量爲 Integer.MAX_VALUE 的 LinkedBlockingQueue。
LinkedBlockingQueue()

// 建立一個容量是 Integer.MAX_VALUE 的 LinkedBlockingQueue,最初包含給定 collection 的元素,元素按該 collection 迭代器的遍歷順序添加。
LinkedBlockingQueue(Collection<? extends E> c)

// 建立一個具備給定(固定)容量的 LinkedBlockingQueue。
LinkedBlockingQueue(int capacity)

// 從隊列完全移除全部元素。
void clear()

// 移除此隊列中全部可用的元素,並將它們添加到給定 collection 中。
int drainTo(Collection<? super E> c)

// 最多今後隊列中移除給定數量的可用元素,並將這些元素添加到給定 collection 中。
int drainTo(Collection<? super E> c, int maxElements)

// 返回在隊列中的元素上按適當順序進行迭代的迭代器。
Iterator<E> iterator()

// 將指定元素插入到此隊列的尾部(若是當即可行且不會超出此隊列的容量),在成功時返回 true,若是此隊列已滿,則返回 false。
boolean offer(E e)

// 將指定元素插入到此隊列的尾部,若有必要,則等待指定的時間以使空間變得可用。
boolean offer(E e, long timeout, TimeUnit unit)

// 獲取但不移除此隊列的頭;若是此隊列爲空,則返回 null。
E peek()

// 獲取並移除此隊列的頭,若是此隊列爲空,則返回 null。
E poll()

// 獲取並移除此隊列的頭部,在指定的等待時間前等待可用的元素(若是有必要)。
E poll(long timeout, TimeUnit unit)

// 將指定元素插入到此隊列的尾部,若有必要,則等待空間變得可用。
void put(E e)

// 返回理想狀況下(沒有內存和資源約束)此隊列可接受而且不會被阻塞的附加元素數量。
int remainingCapacity()

// 今後隊列移除指定元素的單個實例(若是存在)。
boolean remove(Object o)

// 返回隊列中的元素個數。
int size()

// 獲取並移除此隊列的頭部,在元素變得可用以前一直等待(若是有必要)。
E take()

// 返回按適當順序包含此隊列中全部元素的數組。
Object[] toArray()

// 返回按適當順序包含此隊列中全部元素的數組;返回數組的運行時類型是指定數組的運行時類型。
<T> T[] toArray(T[] a)

// 返回此 collection 的字符串表示形式。
String toString()

4、LinkedBlockingQueue示例

import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;

/*
*   LinkedBlockingQueue是「線程安全」的隊列,而LinkedList是非線程安全的。
*
*   下面是「多個線程同時操做而且遍歷queue」的示例
 *   (01) 當queue是LinkedBlockingQueue對象時,程序能正常運行。
*   (02) 當queue是LinkedList對象時,程序會產生ConcurrentModificationException異常。
*
* @author skywang
*/
public class LinkedBlockingQueueDemo {

    // TODO: queue是LinkedList對象時,程序會出錯。
    //private static Queue<String> queue = new LinkedList<String>();
    private static Queue<String> queue = new LinkedBlockingQueue<String>();

    public static void main(String[] args) {

        // 同時啓動兩個線程對queue進行操做!
        new MyThread("ta").start();
        new MyThread("tb").start();
    }

    private static void printAll() {
        String value;
        Iterator iter = queue.iterator();
        while (iter.hasNext()) {
            value = (String) iter.next();
            System.out.print(value + ", ");
        }
        System.out.println();
    }

    private static class MyThread extends Thread {
        MyThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            int i = 0;
            while (i++ < 6) {
                // 「線程名」 + "-" + "序號"
                String val = Thread.currentThread().getName() + i;
                queue.add(val);
                // 經過「Iterator」遍歷queue。
                printAll();
            }
        }
    }
}

運行某一次結果:

ta1, ta1, tb1, 
ta1, tb1, tb2, 
tb1, 
ta1, tb1, tb2, tb3, ta2, 
ta1, tb1, tb2, tb3, ta2, ta1, ta3, tb1, tb2, tb3, ta2, ta3, 
ta1, tb1, tb2, tb3, ta2, ta3, tb4, 
ta1, tb1, tb2, tb3, ta2, ta3, tb4, tb5, 
ta1, tb1, tb2, tb3, ta2, ta3, tb4, tb5, tb6, 

ta1, tb1, tb2, tb3, ta2, ta3, tb4, tb5, tb6, ta4, 
ta1, tb1, tb2, tb3, ta2, ta3, tb4, tb5, tb6, ta4, ta5, 
ta1, tb1, tb2, tb3, ta2, ta3, tb4, tb5, tb6, ta4, ta5, ta6,

結果說明

示例程序中,啓動兩個線程(線程ta和線程tb)分別對LinkedBlockingQueue進行操做。以線程ta而言,它會

先獲取「線程名」+「序號」,而後將該字符串添加到LinkedBlockingQueue中;接着,遍歷並輸出

LinkedBlockingQueue中的所有元素。 線程tb的操做和線程ta同樣,只不過線程tb的名字和線程ta的名字不

同。當queue是LinkedBlockingQueue對象時,程序能正常運行。若是將queue改成LinkedList時,程序會

產生ConcurrentModificationException異常

5、轉載文章

http://www.cnblogs.com/skywang12345/p/3503458.html

相關文章
相關標籤/搜索