J.U.C 阻塞隊(duì)列(二) - ArrayBlockingQueue

1 概述

ArrayBlockingQueued是數(shù)組實(shí)現(xiàn)的線程安全的有界的阻塞隊(duì)列喜命。隊(duì)列按照先進(jìn)先出(FIFO)的原則對(duì)元素進(jìn)行排序。

通過以下關(guān)鍵詞分析我們來更深入理解ArrayBlockingQueue

1.1 如何理解“隊(duì)列”

隊(duì)列這個(gè)概念非常好理解。你可以把它想象成排隊(duì)買票,先來的先買匙瘪,后來的人只能站末尾厢呵,不允許插隊(duì)窝撵。先進(jìn)者先出,這就是典型的“隊(duì)列”襟铭。

相對(duì)于棧只支持兩個(gè)基本操作:入棧 push()和出棧 pop()碌奉,對(duì)于也只支持兩個(gè)操作入隊(duì) enqueue(),放一個(gè)數(shù)據(jù)到隊(duì)列尾部寒砖;出隊(duì) dequeue()赐劣,從隊(duì)列頭部取一個(gè)元素,因此隊(duì)列跟棧一樣哩都,也是一種操作受限的線性表數(shù)據(jù)結(jié)構(gòu)

image
1.2 如何理解“線程安全的”

在多線程情況下魁兼,會(huì)有多個(gè)線程同時(shí)操作隊(duì)列,這個(gè)時(shí)候就會(huì)存在線程安全問題漠嵌,線程安全的隊(duì)列我們叫作并發(fā)隊(duì)列.

那如何實(shí)現(xiàn)一個(gè)線程安全的隊(duì)列呢璃赡?

方式一
最簡單直接的實(shí)現(xiàn)方式是直接在 enqueue()、dequeue() 方法上加鎖献雅,但是鎖粒度大并發(fā)度會(huì)比較低碉考,同一時(shí)刻僅允許一個(gè)存或者取操作。

方式二
利用 CAS 原子操作挺身,可以實(shí)現(xiàn)非常高效的并發(fā)隊(duì)列侯谁。

1.3 阻塞隊(duì)列

阻塞隊(duì)列其實(shí)就是在隊(duì)列基礎(chǔ)上增加了阻塞操作。簡單來說,就是在隊(duì)列為空的時(shí)候墙贱,從隊(duì)頭取數(shù)據(jù)會(huì)被阻塞热芹。因?yàn)榇藭r(shí)還沒有數(shù)據(jù)可取,直到隊(duì)列中有了數(shù)據(jù)才能返回惨撇;如果隊(duì)列已經(jīng)滿了伊脓,那么插入數(shù)據(jù)的操作就會(huì)被阻塞,直到隊(duì)列中有空閑位置后再插入數(shù)據(jù)魁衙,然后再返回报腔。

1.4 有界隊(duì)列

有界隊(duì)列表示隊(duì)列中存儲(chǔ)數(shù)據(jù)是有限,如果隊(duì)列滿后在次向隊(duì)列中添加數(shù)據(jù)會(huì)失敗或阻塞剖淀。

2 實(shí)現(xiàn)一個(gè)"隊(duì)列"

我們知道了纯蛾,隊(duì)列跟棧一樣,也是一種抽象的邏輯存儲(chǔ)結(jié)構(gòu)纵隔。它具有先進(jìn)先出的特性翻诉,支持在隊(duì)尾插入元素,在隊(duì)頭刪除元素捌刮。如果要想實(shí)現(xiàn)一個(gè)隊(duì)列可以用數(shù)組來實(shí)現(xiàn)碰煌,也可以用鏈表來實(shí)現(xiàn),用數(shù)組實(shí)現(xiàn)的隊(duì)列叫作順序隊(duì)列绅作,用鏈表實(shí)現(xiàn)的隊(duì)列叫作鏈?zhǔn)疥?duì)列芦圾。

2.1 順序隊(duì)列
2.1.1 實(shí)現(xiàn)原理

比起棧的數(shù)組實(shí)現(xiàn),隊(duì)列的數(shù)組實(shí)現(xiàn)稍微有點(diǎn)兒復(fù)雜,復(fù)雜在哪呢棚蓄?對(duì)于棧來說堕扶,我們只需要一個(gè)棧頂指針就可以了碍脏。但是隊(duì)列需要兩個(gè)指針:一個(gè)是 head 指針梭依,指向隊(duì)頭;一個(gè)是 tail 指針典尾,指向隊(duì)尾役拴。結(jié)合下面這幅圖來理解。當(dāng) a钾埂、b河闰、c、d 依次入隊(duì)之后褥紫,隊(duì)列中的 head 指針指向下標(biāo)為 0 的位置姜性,tail 指針指向下標(biāo)為 4 的位置。

image

當(dāng)我們調(diào)用兩次出隊(duì)操作之后髓考,隊(duì)列中 head 指針指向下標(biāo)為 2 的位置部念,tail 指針仍然指向下標(biāo)為 4 的位置。

image

你肯定已經(jīng)發(fā)現(xiàn)了,隨著不停地進(jìn)行入隊(duì)儡炼、出隊(duì)操作妓湘,head 和 tail 都會(huì)持續(xù)往后移動(dòng)。當(dāng) tail 移動(dòng)到最右邊乌询,即使數(shù)組中還有空閑空間榜贴,
也無法繼續(xù)往隊(duì)列中添加數(shù)據(jù)了。這個(gè)問題該如何解決呢妹田?使用循環(huán)隊(duì)列

2.3 循環(huán)隊(duì)列

循環(huán)隊(duì)列唬党,顧名思義,它長得像一個(gè)環(huán)秆麸。原本數(shù)組是有頭有尾的初嘹,是一條直線。現(xiàn)在我們把首尾相連沮趣,扳成了一個(gè)環(huán)屯烦。

image

我們可以看到,圖中這個(gè)隊(duì)列的大小為 8房铭,當(dāng)前 head=4驻龟,tail=7。當(dāng)有一個(gè)新的元素 a 入隊(duì)時(shí)缸匪,我們放入下標(biāo)為 7 的位置翁狐。但這個(gè)時(shí)候,我們并不把 tail 更新為 8凌蔬,而是將其在環(huán)中后移一位露懒,到下標(biāo)為 0 的位置。當(dāng)再有一個(gè)元素 b 入隊(duì)時(shí)砂心,我們將 b 放入下標(biāo)為 0 的位置懈词,然后 tail 加 1 更新為 1。所以辩诞,在 a坎弯,b 依次入隊(duì)之后,循環(huán)隊(duì)列中的元素就變成了下面的樣子:

image

通過這樣的方法译暂,我們成功避免了數(shù)據(jù)搬移操作抠忘。但是循環(huán)隊(duì)列最關(guān)鍵的是,確定好隊(duì)空和隊(duì)滿的判定條件外永。

在用數(shù)組實(shí)現(xiàn)的非循環(huán)隊(duì)列中崎脉,隊(duì)滿的判斷條件是 tail == n,隊(duì)空的判斷條件是 head == tail伯顶。那針對(duì)循環(huán)隊(duì)列囚灼,如何判斷隊(duì)空和隊(duì)滿呢呛踊?

方式一

image

就像我圖中畫的隊(duì)滿的情況,tail=3啦撮,head=4谭网,n=8,所以總結(jié)一下規(guī)律就是:(3+1)%8=4赃春。多畫幾張隊(duì)滿的圖愉择,你就會(huì)發(fā)現(xiàn),當(dāng)隊(duì)滿時(shí)滿足以下公式 :(tail+1)%n=head织中。

當(dāng)隊(duì)列滿時(shí)锥涕,圖中的 tail 指向的位置實(shí)際上是沒有存儲(chǔ)數(shù)據(jù)的。所以狭吼,循環(huán)隊(duì)列會(huì)浪費(fèi)一個(gè)數(shù)組的存儲(chǔ)空間层坠。

3 ArrayBlockingQueue源碼解析

3.1 類結(jié)構(gòu)
image
3.2 實(shí)現(xiàn)原理

1 ArrayBlockingQueue使用循環(huán)數(shù)組實(shí)現(xiàn)順序隊(duì)列,

2 ArrayBlockingQueue內(nèi)部存在著一個(gè)可重入的鎖刁笙,當(dāng)出隊(duì)和入隊(duì)操作時(shí)首先要獲取同一把鎖來保證線程安全破花。數(shù)組實(shí)現(xiàn)的隊(duì)列出隊(duì)和入隊(duì)是要相互排斥的,因?yàn)閿?shù)組刪除元素需要對(duì)數(shù)據(jù)中元素進(jìn)行位移疲吸!

3 ArrayBlockingQueue內(nèi)部存在著一個(gè)可重入的鎖座每,同時(shí)此鎖生成二個(gè)等待隊(duì)列Condition(notFull,notEmpty) -- 生產(chǎn)者和消費(fèi)者模式

  • 在入隊(duì)時(shí)需要獲取鎖摘悴,獲取鎖成功后會(huì)判斷隊(duì)列是否已滿(數(shù)組已滿)峭梳。如果隊(duì)列已滿會(huì)將當(dāng)前線程添加到notFull等待隊(duì)列中等待喚醒。如果隊(duì)列沒有滿則入隊(duì)蹂喻,并釋放notEmpty等待隊(duì)列中一個(gè)等待的線程葱椭。

  • 在出隊(duì)時(shí)需要獲取鎖,獲取鎖成功后會(huì)判斷隊(duì)列是否為空(數(shù)組為空)口四。如果隊(duì)列為空會(huì)將當(dāng)前線程添加到notEmpty等待隊(duì)列中等待喚醒孵运。如果隊(duì)列不為空則出隊(duì),并釋放notFull等待隊(duì)列中一個(gè)等待的線程窃祝。

3.3 核心屬性
// 內(nèi)部數(shù)組
final Object[] items;

// 重入鎖
final ReentrantLock lock;

// 條件掐松,用于出隊(duì)等待隊(duì)列
private final Condition notEmpty;

// 條件踱侣,用于入隊(duì)等待隊(duì)列
private final Condition notFull;

// 出隊(duì)位置
int takeIndex;

// 入隊(duì)位置
int putIndex;

//隊(duì)列中元素?cái)?shù)量
int count;
3.4 構(gòu)造函數(shù)
//創(chuàng)建一個(gè)帶有給定的(固定)容量和默認(rèn)訪問策略的 ArrayBlockingQueue粪小。
public ArrayBlockingQueue(int capacity) {
    this(capacity, false);
}
    
//創(chuàng)建一個(gè)具有給定的(固定)容量和指定訪問策略的 ArrayBlockingQueue。
//fair 用來表示獲取鎖的方式是否公平[(詳見ReentrantLock)](https://note.youdao.com/)
public ArrayBlockingQueue(int capacity, boolean fair) {
    if (capacity <= 0)
        throw new IllegalArgumentException();
    this.items = new Object[capacity];
    lock = new ReentrantLock(fair);
    notEmpty = lock.newCondition();
    notFull =  lock.newCondition();
}

//創(chuàng)建一個(gè)具有給定的(固定)容量和指定訪問策略的 ArrayBlockingQueue抡句,它最初包含給定 collection 的元素探膊,并以 collection 迭代器的遍歷順序添加元素。
public ArrayBlockingQueue(int capacity, boolean fair,
                              Collection<? extends E> c) {
        this(capacity, fair);
        //將指定容器中元素依此添加到隊(duì)列中
        final ReentrantLock lock = this.lock;
        //獲取獨(dú)占鎖待榔,成功返回逞壁,失敗則阻塞
        lock.lock();  
        try {
            int i = 0;
            try {
                //遍歷容器張的元素添加到隊(duì)列中
                for (E e : c) {
                    //檢查添加數(shù)據(jù)不能為null,NullPointerException
                    checkNotNull(e);
                    items[i++] = e;
                }
            } catch (ArrayIndexOutOfBoundsException ex) {
                throw new IllegalArgumentException();
            }
            count = i;
            putIndex = (i == capacity) ? 0 : i;
        } finally {
            lock.unlock();
        }
    }
3.5 入隊(duì)操作

將指定的元素插入到此隊(duì)列的尾部(如果立即可行且不會(huì)超過該隊(duì)列的容量)流济,在成功時(shí)返回 true,如果此隊(duì)列已滿腌闯,則拋出 IllegalStateException绳瘟。

public boolean add(E e) {
    if (offer(e)){
        return true;
    }else{
        // 拋出異常...
    }
}

將指定的元素插入到此隊(duì)列的尾部(如果立即可行且不會(huì)超過該隊(duì)列的容量),在成功時(shí)返回 true姿骏,如果此隊(duì)列已滿糖声,則返回 false。

public boolean offer(E e) {
    //插入的元素是否為null分瘦,是拋出NullPointerException異常
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    //獲取獨(dú)占鎖蘸泻,成功返回,失敗則阻塞
    lock.lock();

    try {
        //如果隊(duì)列已滿嘲玫,則返回false悦施。
        if (count == items.length){
            return false;
        }else {
            //添加元素到隊(duì)列尾部
            enqueue(e);
            return true;
        }
    } finally {
        //釋放鎖
        lock.unlock();
    }
}

//添加元素到隊(duì)列尾部
private void enqueue(E x) {
        //獲取隊(duì)列中數(shù)組對(duì)象
        final Object[] items = this.items;
        //在items[putIndex]插入元素
        items[putIndex] = x;
        //putIndex向數(shù)組尾部循環(huán)移動(dòng)
        if (++putIndex == items.length)
            putIndex = 0;
        //隊(duì)列元素?cái)?shù)量+1    
        count++;
        //釋放notEmpty等待隊(duì)列中阻塞的線程
        notEmpty.signal();
    }

將指定的元素插入此隊(duì)列的尾部,如果該隊(duì)列已滿去团,則在到達(dá)指定的等待時(shí)間之前等待可用的空間抡诞,在成功時(shí)返回 true。

public boolean offer(E e, long timeout, TimeUnit unit)
        throws InterruptedException {
        //插入的元素是否為null土陪,是拋出NullPointerException異常
        checkNotNull(e);
        //獲取等待的時(shí)間
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        //獲取獨(dú)占鎖沐绒,成功返回,失敗則阻塞旺坠,可響應(yīng)中斷
        lock.lockInterruptibly();
        try {
            //如果隊(duì)列已滿乔遮,等待時(shí)間大于0。將當(dāng)前線程添加notFull等待隊(duì)列取刃,并到限時(shí)阻塞當(dāng)前線程
            while (count == items.length) {
                if (nanos <= 0)
                    return false;
                nanos = notFull.awaitNanos(nanos);
            }
            //添加元素到隊(duì)列尾部
            enqueue(e);
            //成功返回true
            return true;
        } finally {
            //釋放鎖
            lock.unlock();
        }
    }

將指定的元素插入此隊(duì)列的尾部蹋肮,如果該隊(duì)列已滿,則等待可用的空間

public void put(E e) throws InterruptedException {
    //插入的元素是否為null璧疗,是拋出NullPointerException異常
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    //獲取鎖坯辩,失敗則阻塞,可響應(yīng)中斷崩侠。
    lock.lockInterruptibly();
    try {
        //如果隊(duì)列已滿漆魔。將當(dāng)前線程添加notFull等待隊(duì)列,并阻塞當(dāng)前線程
        while (count == items.length){
            notFull.await();
        }
        //添加元素到隊(duì)列
       enqueue(e);
    } finally {
        //釋放鎖
        lock.unlock();
    }
}
3.6 出隊(duì)操作

獲取并移除此隊(duì)列的頭却音,如果此隊(duì)列為空改抡,則返回 null。

public E poll() {
    final ReentrantLock lock = this.lock;
    //獲取鎖系瓢,失敗則阻塞阿纤。
    lock.lock();
    try {
        //若隊(duì)列為空,則返回 null夷陋。
        //若隊(duì)列不為空欠拾,返回隊(duì)列頭部元素
        return (count == 0) ? null : dequeue();
    } finally {
        //釋放鎖
        lock.unlock();
    }
}

//返回隊(duì)列頭部元素
private E dequeue() {
        //獲取數(shù)組對(duì)象
        final Object[] items = this.items;
        //獲取items[takeIndex]元素
        E x = (E) items[takeIndex];
        //將數(shù)組items[takeIndex]元素設(shè)置為null
        items[takeIndex] = null;
        //takeIndex向后循環(huán)移動(dòng)
        if (++takeIndex == items.length)
            takeIndex = 0;
        //隊(duì)列數(shù)量--    
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        //釋放等待notFull等待隊(duì)列中阻塞的線程    
        notFull.signal();
        //返回
        return x;
    }

獲取并移除此隊(duì)列的頭部胰锌,在指定的等待時(shí)間前等待可用的元素(如果有必要)。

public E poll(long timeout, TimeUnit unit) throws InterruptedException {
        //獲取等待的時(shí)間
        long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        //獲取鎖藐窄,失敗則阻塞资昧,可響應(yīng)中斷。
        lock.lockInterruptibly();
        try {
            //如果隊(duì)列為空荆忍,等待時(shí)間大于0榛搔,將當(dāng)前線程添加notEmpty等待隊(duì)列,并限時(shí)阻塞當(dāng)前線程
            while (count == 0) {
                if (nanos <= 0)
                    return null;
                nanos = notEmpty.awaitNanos(nanos);
            }
            //返回隊(duì)列頭部元素
            return dequeue();
        } finally {
            //釋放鎖
            lock.unlock();
        }
    }

獲取并移除此隊(duì)列的頭部东揣,在元素變得可用之前一直等待

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    //獲取鎖践惑,失敗則阻塞,可響應(yīng)中斷嘶卧。
    lock.lockInterruptibly();
    try {
        //如果隊(duì)列為空尔觉,將當(dāng)前線程添加notEmpty等待隊(duì)列張,并阻塞當(dāng)前線程
        while (count == 0){
            notEmpty.await();
        }
        //返回隊(duì)列頭部元素
        return dequeue();
    } finally {
        //釋放鎖
        lock.unlock();
    }
}


指定元素出隊(duì)

  • 從此隊(duì)列中移除指定元素的單個(gè)實(shí)例
    • 遍歷隊(duì)列數(shù)組中元素所有元素找到指定元素在數(shù)組的下標(biāo)
    • 如果刪除元素剛好位置和takeIndex重合芥吟,直接刪除侦铜,takeIndex移動(dòng)


      image
    • 刪除元素b剛好位置在takeIndex和putIndex之間,直接刪除钟鸵,并將數(shù)組向前平移钉稍。


      image
public boolean remove(Object o) {
    //刪除元素為null,直接返回失敗
    if (o == null){
        return false;
    }
    final Object[] items = this.items;
    final ReentrantLock lock = this.lock;
    //獲取鎖,失敗則阻塞
    lock.lock();
    try {
        // 遍歷內(nèi)部數(shù)組的所有元素棺耍,從 takeIndex 開始贡未,找到刪除元素的下標(biāo)位置
        for (int i = takeIndex, k = count; k > 0; i = inc(i), k--) {
            if (o.equals(items[i])) {
                //刪除指定下標(biāo)元素
                removeAt(i);
                return true;
            }
        }
        return false;
    } finally {
        //釋放鎖
        lock.unlock();
    }
}

//刪除指定下標(biāo)元素
void removeAt(final int removeIndex) {
        final Object[] items = this.items;
        // 判斷是否在刪除元素的下標(biāo)是否和takeIndex重合
        if (removeIndex == takeIndex) {
            //清理takeIndex下標(biāo)中的元素
            items[takeIndex] = null;
            //移動(dòng)takeIndex
            if (++takeIndex == items.length)
                takeIndex = 0;
            count--;
            
            if (itrs != null)
                itrs.elementDequeued();
        } else {
            // 當(dāng)和takeIndex不重合,刪除的下標(biāo)在takeIndex~putIndex之間蒙袍。
            // 需要將 [(i+1) - putIndex ] 位置的元素俊卤,前移一位,通過覆蓋 i 位置的元素害幅,來達(dá)到刪除的效果消恍。
            final int putIndex = this.putIndex;
            for (int i = removeIndex;;) {
                int next = i + 1;
                if (next == items.length)
                    next = 0;
                if (next != putIndex) {
                    items[i] = items[next];
                    i = next;
                } else {
                    items[i] = null;
                    this.putIndex = i;
                    break;
                }
            }
            count--;
            if (itrs != null)
                itrs.removedAt(removeIndex);
        }
        //釋放等待notFull等待隊(duì)列中阻塞的線程  
        notFull.signal();
    }

4 ArrayBlockingQueue使用

4.1 ArrayBlockingQueue API
// 創(chuàng)建一個(gè)帶有給定的(固定)容量和默認(rèn)訪問策略的 ArrayBlockingQueue。
ArrayBlockingQueue(int capacity)
// 創(chuàng)建一個(gè)具有給定的(固定)容量和指定訪問策略的 ArrayBlockingQueue以现。
ArrayBlockingQueue(int capacity, boolean fair)
// 創(chuàng)建一個(gè)具有給定的(固定)容量和指定訪問策略的 ArrayBlockingQueue狠怨,它最初包含給定 collection 的元素,并以 collection 迭代器的遍歷順序添加元素邑遏。
ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c)

// 將指定的元素插入到此隊(duì)列的尾部(如果立即可行且不會(huì)超過該隊(duì)列的容量)佣赖,在成功時(shí)返回 true,如果此隊(duì)列已滿无宿,則拋出 IllegalStateException茵汰。
boolean add(E e)
// 自動(dòng)移除此隊(duì)列中的所有元素枢里。
void clear()
// 如果此隊(duì)列包含指定的元素孽鸡,則返回 true蹂午。
boolean contains(Object o)
// 移除此隊(duì)列中所有可用的元素,并將它們添加到給定 collection 中彬碱。
int drainTo(Collection<? super E> c)
// 最多從此隊(duì)列中移除給定數(shù)量的可用元素豆胸,并將這些元素添加到給定 collection 中。
int drainTo(Collection<? super E> c, int maxElements)
// 返回在此隊(duì)列中的元素上按適當(dāng)順序進(jìn)行迭代的迭代器巷疼。
Iterator<E> iterator()
// 將指定的元素插入到此隊(duì)列的尾部(如果立即可行且不會(huì)超過該隊(duì)列的容量)晚胡,在成功時(shí)返回 true,如果此隊(duì)列已滿嚼沿,則返回 false估盘。
boolean offer(E e)
// 將指定的元素插入此隊(duì)列的尾部,如果該隊(duì)列已滿骡尽,則在到達(dá)指定的等待時(shí)間之前等待可用的空間遣妥。
boolean offer(E e, long timeout, TimeUnit unit)
// 獲取但不移除此隊(duì)列的頭;如果此隊(duì)列為空攀细,則返回 null箫踩。
E peek()
// 獲取并移除此隊(duì)列的頭,如果此隊(duì)列為空谭贪,則返回 null境钟。
E poll()
// 獲取并移除此隊(duì)列的頭部,在指定的等待時(shí)間前等待可用的元素(如果有必要)俭识。
E poll(long timeout, TimeUnit unit)
// 將指定的元素插入此隊(duì)列的尾部慨削,如果該隊(duì)列已滿,則等待可用的空間套媚。
void put(E e)
// 返回在無阻塞的理想情況下(不存在內(nèi)存或資源約束)此隊(duì)列能接受的其他元素?cái)?shù)量理盆。
int remainingCapacity()
// 從此隊(duì)列中移除指定元素的單個(gè)實(shí)例(如果存在)。
boolean remove(Object o)
// 返回此隊(duì)列中元素的數(shù)量凑阶。
int size()
// 獲取并移除此隊(duì)列的頭部猿规,在元素變得可用之前一直等待(如果有必要)。
E take()
// 返回一個(gè)按適當(dāng)順序包含此隊(duì)列中所有元素的數(shù)組宙橱。
Object[] toArray()
// 返回一個(gè)按適當(dāng)順序包含此隊(duì)列中所有元素的數(shù)組姨俩;返回?cái)?shù)組的運(yùn)行時(shí)類型是指定數(shù)組的運(yùn)行時(shí)類型。
<T> T[] toArray(T[] a)
// 返回此 collection 的字符串表示形式师郑。
String toString()
4.2 案例
import java.util.*;
import java.util.concurrent.*;

/*
 *   ArrayBlockingQueue是“線程安全”的隊(duì)列环葵,而LinkedList是非線程安全的。
 *
 *   下面是“多個(gè)線程同時(shí)操作并且遍歷queue”的示例
 *   (01) 當(dāng)queue是ArrayBlockingQueue對(duì)象時(shí)宝冕,程序能正常運(yùn)行张遭。
 *   (02) 當(dāng)queue是LinkedList對(duì)象時(shí),程序會(huì)產(chǎn)生ConcurrentModificationException異常地梨。
 *
 */
public class ArrayBlockingQueueDemo1{

    // TODO: queue是LinkedList對(duì)象時(shí)菊卷,程序會(huì)出錯(cuò)缔恳。
    //private static Queue<String> queue = new LinkedList<String>();
    private static Queue<String> queue = new ArrayBlockingQueue<String>(20);
    public static void main(String[] args) {
    
        // 同時(shí)啟動(dòng)兩個(gè)線程對(duì)queue進(jìn)行操作!
        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) {
                // “線程名” + "-" + "序號(hào)"
                String val = Thread.currentThread().getName()+i;
                queue.add(val);
                // 通過“Iterator”遍歷queue洁闰。
                printAll();
            }
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末歉甚,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子扑眉,更是在濱河造成了極大的恐慌纸泄,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件腰素,死亡現(xiàn)場離奇詭異聘裁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)弓千,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門咧虎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人计呈,你說我怎么就攤上這事砰诵。” “怎么了捌显?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵茁彭,是天一觀的道長。 經(jīng)常有香客問我扶歪,道長理肺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任善镰,我火速辦了婚禮妹萨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘炫欺。我一直安慰自己乎完,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布品洛。 她就那樣靜靜地躺著树姨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪桥状。 梳的紋絲不亂的頭發(fā)上帽揪,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天,我揣著相機(jī)與錄音辅斟,去河邊找鬼转晰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的查邢。 我是一名探鬼主播蔗崎,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼侠坎!你這毒婦竟也來了蚁趁?” 一聲冷哼從身側(cè)響起裙盾,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤实胸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后番官,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體庐完,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年徘熔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了门躯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡酷师,死狀恐怖讶凉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情山孔,我是刑警寧澤懂讯,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站台颠,受9級(jí)特大地震影響褐望,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜串前,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一瘫里、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧荡碾,春花似錦谨读、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至阶冈,卻和暖如春闷尿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背女坑。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來泰國打工填具, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓劳景,卻偏偏與公主長得像誉简,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子盟广,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

推薦閱讀更多精彩內(nèi)容