多線(xiàn)程之阻塞隊(duì)列

轉(zhuǎn)載自:http://www.cnblogs.com/dolphin0520/p/3932906.html
一右锨、阻塞隊(duì)列:
對(duì)隊(duì)列阻塞寨蹋,實(shí)現(xiàn)消費(fèi)者-生產(chǎn)者模型陵且。
阻塞隊(duì)列為于juc包下破花。
二禁添、阻塞隊(duì)列方法和非阻塞隊(duì)列方法
1.非阻塞隊(duì)列中的幾個(gè)主要方法:

add(E e):將元素e插入到隊(duì)列末尾纠炮,如果插入成功,則返回true;如果插入失斢糖邸(即隊(duì)列已滿(mǎn)),則會(huì)拋出異常鞠绰;

remove():移除隊(duì)首元素腰埂,若移除成功,則返回true蜈膨;如果移除失斢炝(隊(duì)列為空),則會(huì)拋出異常翁巍;

offer(E e):將元素e插入到隊(duì)列末尾驴一,如果插入成功,則返回true灶壶;如果插入失敻味稀(即隊(duì)列已滿(mǎn)),則返回false驰凛;

poll():移除并獲取隊(duì)首元素胸懈,若成功,則返回隊(duì)首元素洒嗤;否則返回null箫荡;

peek():獲取隊(duì)首元素魁亦,若成功渔隶,則返回隊(duì)首元素;否則返回null

對(duì)于非阻塞隊(duì)列洁奈,一般情況下建議使用offer间唉、poll和peek三個(gè)方法,不建議使用add和remove方法利术。因?yàn)槭褂胦ffer呈野、poll和peek三個(gè)方法可以通過(guò)返回值判斷操作成功與否,而使用add和remove方法卻不能達(dá)到這樣的效果印叁。注意被冒,非阻塞隊(duì)列中的方法都沒(méi)有進(jìn)行同步措施。

2.阻塞隊(duì)列中的幾個(gè)主要方法:

阻塞隊(duì)列包括了非阻塞隊(duì)列中的大部分方法轮蜕,上面列舉的5個(gè)方法在阻塞隊(duì)列中都存在昨悼,但是要注意這5個(gè)方法在阻塞隊(duì)列中都進(jìn)行了同步措施。除此之外跃洛,阻塞隊(duì)列提供了另外4個(gè)非常有用的方法:

put(E e)

take()

offer(E e,long timeout, TimeUnit unit)

poll(long timeout, TimeUnit unit)

put方法用來(lái)向隊(duì)尾存入元素率触,如果隊(duì)列滿(mǎn),則等待汇竭;

take方法用來(lái)從隊(duì)首取元素葱蝗,如果隊(duì)列為空穴张,則等待;

offer方法用來(lái)向隊(duì)尾存入元素两曼,如果隊(duì)列滿(mǎn)皂甘,則等待一定的時(shí)間,當(dāng)時(shí)間期限達(dá)到時(shí)悼凑,如果還沒(méi)有插入成功叮贩,則返回false;否則返回true佛析;

poll方法用來(lái)從隊(duì)首取元素益老,如果隊(duì)列空,則等待一定的時(shí)間寸莫,當(dāng)時(shí)間期限達(dá)到時(shí)捺萌,如果取到,則返回null膘茎;否則返回取得的元素桃纯;
**三:阻塞隊(duì)列實(shí)現(xiàn)原理

public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
 
private static final long serialVersionUID = -817911632652898426L;
 
/** The queued items  */
private final E[] items;
/** items index for next take, poll or remove */
private int takeIndex;
/** items index for next put, offer, or add. */
private int putIndex;
/** Number of items in the queue */
private int count;
 
/*
* Concurrency control uses the classic two-condition algorithm
* found in any textbook.
*/
 
/** Main lock guarding all access */
private final ReentrantLock lock;
/** Condition for waiting takes */
private final Condition notEmpty;
/** Condition for waiting puts */
private final Condition notFull;
}

可以看出,ArrayBlockingQueue中用來(lái)存儲(chǔ)元素的實(shí)際上是一個(gè)數(shù)組披坏,takeIndex和putIndex分別表示隊(duì)首元素和隊(duì)尾元素的下標(biāo)态坦,count表示隊(duì)列中元素的個(gè)數(shù)。

lock是一個(gè)可重入鎖棒拂,notEmpty和notFull是等待條件伞梯。
下面看一下ArrayBlockingQueue的構(gòu)造器,構(gòu)造器有三個(gè)重載版本:

public ArrayBlockingQueue(int capacity) {
}
public ArrayBlockingQueue(int capacity, boolean fair) {
 
}
public ArrayBlockingQueue(int capacity, boolean fair,
                          Collection<? extends E> c) {
}

然后看它的兩個(gè)關(guān)鍵方法的實(shí)現(xiàn):put()和take():

public void put(E e) throws InterruptedException {
    if (e == null) throw new NullPointerException();
    final E[] items = this.items;
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        try {
            while (count == items.length)
                notFull.await();
        } catch (InterruptedException ie) {
            notFull.signal(); // propagate to non-interrupted thread
            throw ie;
        }
        insert(e);
    } finally {
        lock.unlock();
    }
}

從put方法的實(shí)現(xiàn)可以看出帚屉,它先獲取了鎖谜诫,并且獲取的是可中斷鎖,然后判斷當(dāng)前元素個(gè)數(shù)是否等于數(shù)組的長(zhǎng)度攻旦,如果相等喻旷,則調(diào)用notFull.await()進(jìn)行等待,如果捕獲到中斷異常牢屋,則喚醒線(xiàn)程并拋出異常且预。

當(dāng)被其他線(xiàn)程喚醒時(shí),通過(guò)insert(e)方法插入元素烙无,最后解鎖锋谐。

我們看一下insert方法的實(shí)現(xiàn):

private void insert(E x) {
    items[putIndex] = x;
    putIndex = inc(putIndex);
    ++count;
    notEmpty.signal();
}

它是一個(gè)private方法,插入成功后皱炉,通過(guò)notEmpty喚醒正在等待取元素的線(xiàn)程怀估。
下面是take()方法的實(shí)現(xiàn):

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        try {
            while (count == 0)
                notEmpty.await();
        } catch (InterruptedException ie) {
            notEmpty.signal(); // propagate to non-interrupted thread
            throw ie;
        }
        E x = extract();
        return x;
    } finally {
        lock.unlock();
    }
}

跟put方法實(shí)現(xiàn)很類(lèi)似,只不過(guò)put方法等待的是notFull信號(hào),而take方法等待的是notEmpty信號(hào)多搀。在take方法中歧蕉,如果可以取元素,則通過(guò)extract方法取得元素康铭,下面是extract方法的實(shí)現(xiàn):

private E extract() {
    final E[] items = this.items;
    E x = items[takeIndex];
    items[takeIndex] = null;
    takeIndex = inc(takeIndex);
    --count;
    notFull.signal();
    return x;
}

跟insert方法也很類(lèi)似惯退。

其實(shí)從這里大家應(yīng)該明白了阻塞隊(duì)列的實(shí)現(xiàn)原理,事實(shí)它和我們用Object.wait()从藤、Object.notify()和非阻塞隊(duì)列實(shí)現(xiàn)生產(chǎn)者-消費(fèi)者的思路類(lèi)似催跪,只不過(guò)它把這些工作一起集成到了阻塞隊(duì)列中實(shí)現(xiàn)。
四:示例和使用場(chǎng)景
普通隊(duì)列實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模型

public class Test {
    private int queueSize = 10;
    private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);
     
    public static void main(String[] args)  {
        Test test = new Test();
        Producer producer = test.new Producer();
        Consumer consumer = test.new Consumer();
         
        producer.start();
        consumer.start();
    }
     
    class Consumer extends Thread{
         
        @Override
        public void run() {
            consume();
        }
         
        private void consume() {
            while(true){
                synchronized (queue) {
                    while(queue.size() == 0){
                        try {
                            System.out.println("隊(duì)列空夷野,等待數(shù)據(jù)");
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notify();
                        }
                    }
                    queue.poll();          //每次移走隊(duì)首元素
                    queue.notify();
                    System.out.println("從隊(duì)列取走一個(gè)元素懊蒸,隊(duì)列剩余"+queue.size()+"個(gè)元素");
                }
            }
        }
    }
     
    class Producer extends Thread{
         
        @Override
        public void run() {
            produce();
        }
         
        private void produce() {
            while(true){
                synchronized (queue) {
                    while(queue.size() == queueSize){
                        try {
                            System.out.println("隊(duì)列滿(mǎn),等待有空余空間");
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notify();
                        }
                    }
                    queue.offer(1);        //每次插入一個(gè)元素
                    queue.notify();
                    System.out.println("向隊(duì)列取中插入一個(gè)元素悯搔,隊(duì)列剩余空間:"+(queueSize-queue.size()));
                }
            }
        }
    }
}

阻塞隊(duì)列實(shí)現(xiàn)

public class Tttt {
    ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(10);

    class Consumer1 extends Thread{
        @Override
        public void run() {
            consume();
        }
        private void consume(){
            while (true){
                try {
                    queue.take();
                    System.out.println("從隊(duì)列中取走一個(gè)骑丸,剩下"+queue.size());
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
    }
    class Producer extends Thread{
        @Override
        public void run() {
            produce();
        }
        private void produce(){
            while (true){
                try {
                    queue.put(1);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) {
           Tttt test = new Tttt();
        Producer producer = test.new Producer();
        Consumer1 consumer1 = test.new Consumer1();
        producer.start();
        consumer1.start();

    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市妒貌,隨后出現(xiàn)的幾起案子通危,更是在濱河造成了極大的恐慌,老刑警劉巖灌曙,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件菊碟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡在刺,警方通過(guò)查閱死者的電腦和手機(jī)逆害,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)增炭,“玉大人忍燥,你說(shuō)我怎么就攤上這事∠蹲耍” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵厂捞,是天一觀的道長(zhǎng)输玷。 經(jīng)常有香客問(wèn)我,道長(zhǎng)靡馁,這世上最難降的妖魔是什么欲鹏? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮臭墨,結(jié)果婚禮上赔嚎,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好尤误,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布侠畔。 她就那樣靜靜地躺著,像睡著了一般损晤。 火紅的嫁衣襯著肌膚如雪软棺。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,784評(píng)論 1 290
  • 那天尤勋,我揣著相機(jī)與錄音喘落,去河邊找鬼。 笑死最冰,一個(gè)胖子當(dāng)著我的面吹牛瘦棋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播暖哨,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼兽狭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了鹿蜀?” 一聲冷哼從身側(cè)響起箕慧,我...
    開(kāi)封第一講書(shū)人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎茴恰,沒(méi)想到半個(gè)月后颠焦,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡往枣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年伐庭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片分冈。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡圾另,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出雕沉,到底是詐尸還是另有隱情集乔,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布坡椒,位于F島的核電站扰路,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏倔叼。R本人自食惡果不足惜汗唱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望丈攒。 院中可真熱鬧哩罪,春花似錦授霸、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至腹鹉,卻和暖如春藏畅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背功咒。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工愉阎, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人力奋。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓榜旦,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親景殷。 傳聞我的和親對(duì)象是個(gè)殘疾皇子溅呢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

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

  • 一基本概念:1:什么叫阻塞隊(duì)列阻塞隊(duì)列都是相對(duì)于非阻塞隊(duì)列而言的,非阻塞隊(duì)列就是隊(duì)列不會(huì)對(duì)當(dāng)前線(xiàn)程產(chǎn)生阻塞猿挚;例如當(dāng)...
    cp_insist閱讀 404評(píng)論 0 0
  • 阻塞隊(duì)列 BlockingQueue 隊(duì)列主要有兩種:FIFO(先進(jìn)先出)咐旧、LIFO(后進(jìn)先出)。再多線(xiàn)程環(huán)境中绩蜻,...
    OPice閱讀 752評(píng)論 0 1
  • Java平臺(tái)類(lèi)庫(kù)包含了豐富的并發(fā)基礎(chǔ)構(gòu)建模塊铣墨,例如線(xiàn)程安全的容器類(lèi)以及各種用于協(xié)調(diào)多個(gè)相互協(xié)作的線(xiàn)程控制流的同步工...
    Steven1997閱讀 555評(píng)論 0 0
  • 天之采采, 云若粼粼办绝。 陽(yáng)劍如柱伊约, 念美人圖。 天之藍(lán)藍(lán)孕蝉, 云若霧紗屡律。 秋陽(yáng)如披, 念美人顏降淮。 天之清清超埋, 云若綢...
    清澗騷人閱讀 181評(píng)論 0 0
  • 今日體驗(yàn):不管做任何事都要把話(huà)說(shuō)明白,只有對(duì)方明白了你所說(shuō)的話(huà)自然也就會(huì)相信你骤肛。
    其實(shí)_1d17閱讀 25評(píng)論 0 0