LinkedBlockingDeque阻塞隊(duì)列

一敞恋、簡(jiǎn)述

LinkedBlockingDeque 是一個(gè)由鏈表結(jié)構(gòu)組成的雙向阻塞隊(duì)列其兴,即可以從隊(duì)列的兩端插入和移除元素猪瞬。雙向隊(duì)列因?yàn)槎嗔艘粋€(gè)操作隊(duì)列的入口链韭,在多線程同時(shí)入隊(duì)時(shí),也就減少了一半的競(jìng)爭(zhēng)庭惜。相比于其他阻塞隊(duì)列距芬,LinkedBlockingDeque 多了 addFirst涝开、addLast、peekFirst框仔、peekLast 等方法舀武。以first結(jié)尾的方法,表示插入离斩、獲取或移除雙端隊(duì)列的第一個(gè)元素银舱。以 last 結(jié)尾的方法,表示插入跛梗、獲取或移除雙端隊(duì)列的最后一個(gè)元素纵朋。LinkedBlockingDeque 是可選容量的,在初始化時(shí)可以設(shè)置容量防止其過(guò)度膨脹茄袖。如果不設(shè)置,默認(rèn)容量大小為 Integer.MAX_VALUE嘁锯。LinkedBlockingDeque 類有三個(gè)構(gòu)造方法:

public LinkedBlockingDeque()
public LinkedBlockingDeque(int capacity)
public LinkedBlockingDeque(Collection<? extends E> c)

二宪祥、LinkedBlockingDeque源碼詳解

LinkedBlockingDeque 類定義為:

public class LinkedBlockingDeque<E>
    extends AbstractQueue<E>
    implements BlockingDeque<E>, java.io.Serializable

該類繼承自 AbstractQueue 抽象類,又實(shí)現(xiàn)了 BlockingDeque 接口家乘。BlockingDeque 接口定義如下:

public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E>

BlockingDeque 繼承自 BlockingQueue 和 Deque 接口蝗羊,BlockingDeque 接口定義了在雙端隊(duì)列中常用的方法。

LinkedBlockingDeque 類中的數(shù)據(jù)都被封裝成了 Node 對(duì)象:

 static final class Node<E> {
        E item;
        Node<E> prev;
        Node<E> next;
        Node(E x) {
            item = x;
        }
    }

LinkedBlockingDeque 類中的重要字段如下:

// 隊(duì)列雙向鏈表首節(jié)點(diǎn)
transient Node<E> first;
// 隊(duì)列雙向鏈表尾節(jié)點(diǎn)
transient Node<E> last;
// 雙向鏈表元素個(gè)數(shù)
private transient int count;
// 雙向鏈表最大容量
private final int capacity;
// 全局獨(dú)占鎖
final ReentrantLock lock = new ReentrantLock();
// 非空Condition對(duì)象
private final Condition notEmpty = lock.newCondition();
// 非滿Condition對(duì)象
private final Condition notFull = lock.newCondition();

LinkedBlockingDeque 類的底層實(shí)現(xiàn)和 LinkedBlockingQueue 類很相似仁锯,都有一個(gè)全局獨(dú)占鎖耀找,和兩個(gè) Condition 對(duì)象,用來(lái)阻塞和喚醒線程。

LinkedBlockingDeque 類對(duì)元素的操作方法比較多野芒。針對(duì)元素的入隊(duì)蓄愁、出隊(duì)操作以 putFirst、putLast狞悲、pollFirst撮抓、pollLast 方法來(lái)進(jìn)行分析。

三摇锋、入隊(duì)

1??putFirst(E e) 是將指定的元素插入雙端隊(duì)列的開頭丹拯,源碼如下:

public void putFirst(E e) throws InterruptedException {
    // 若插入元素為null,則直接拋出NullPointerException異常
    if (e == null) throw new NullPointerException();
    // 將插入節(jié)點(diǎn)包裝為Node節(jié)點(diǎn)
    Node<E> node = new Node<E>(e);
    // 獲取全局獨(dú)占鎖
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        while (!linkFirst(node))
            notFull.await();
    } finally {
        // 釋放全局獨(dú)占鎖
        lock.unlock();
    }
}

2??入隊(duì)操作是通過(guò) linkFirst(E e) 來(lái)完成的荸恕,如下所示:

private boolean linkFirst(Node<E> node) {
    // assert lock.isHeldByCurrentThread();
    // 元素個(gè)數(shù)超出容量乖酬。直接返回false
    if (count >= capacity)
        return false;
    // 獲取雙向鏈表的首節(jié)點(diǎn)
    Node<E> f = first;
    // 將node設(shè)置為首節(jié)點(diǎn)
    node.next = f;
    first = node;
    // 若last為null,設(shè)置尾節(jié)點(diǎn)為node節(jié)點(diǎn)
    if (last == null)
        last = node;
    else
        // 更新原首節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)
        f.prev = node;
    ++count;
    // 喚醒阻塞在notEmpty上的線程
    notEmpty.signal();
    return true;
}

若入隊(duì)成功融求,則 linkFirst(E e) 返回 true咬像,否則返回 false。若該方法返回 false双肤,則當(dāng)前線程會(huì)阻塞在 notFull 條件上施掏。

3??putLast(E e) 是將指定的元素插入到雙端隊(duì)列的末尾,源碼如下:

public void putLast(E e) throws InterruptedException {
    // 若插入元素為null茅糜,則直接拋出NullPointerException異常
    if (e == null) throw new NullPointerException();
    // 將插入節(jié)點(diǎn)包裝為Node節(jié)點(diǎn)
    Node<E> node = new Node<E>(e);
    // 獲取全局獨(dú)占鎖
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        while (!linkLast(node))
            notFull.await();
    } finally {
        // 釋放全局獨(dú)占鎖
        lock.unlock();
    }
}

4??該方法和 putFirst(E e) 幾乎一樣七芭,不同點(diǎn)在于,putLast(E e) 通過(guò)調(diào)用 linkLast(E e) 來(lái)插入節(jié)點(diǎn):

private boolean linkLast(Node<E> node) {
    // assert lock.isHeldByCurrentThread();
    // 元素個(gè)數(shù)超出容量蔑赘。直接返回false
    if (count >= capacity)
        return false;
    // 獲取雙向鏈表的尾節(jié)點(diǎn)
    Node<E> l = last;
    // 將node設(shè)置為尾節(jié)點(diǎn)
    node.prev = l;
    last = node;
    // 若first為null狸驳,設(shè)置首節(jié)點(diǎn)為node節(jié)點(diǎn)
    if (first == null)
        first = node;
    else
        // 更新原尾節(jié)點(diǎn)的后繼節(jié)點(diǎn)
        l.next = node;
    ++count;
    // 喚醒阻塞在notEmpty上的線程
    notEmpty.signal();
    return true;
}

若入隊(duì)成功,則 linkLast(E e) 返回 true缩赛,否則返回 false耙箍。若該方法返回 false,則當(dāng)前線程會(huì)阻塞在 notFull 條件上酥馍。

四辩昆、出隊(duì)

1??pollFirst() 是獲取并移除此雙端隊(duì)列的首節(jié)點(diǎn),若不存在旨袒,則返回 null汁针,源碼如下:

public E pollFirst() {
    // 獲取全局獨(dú)占鎖
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        return unlinkFirst();
    } finally {
        // 釋放全局獨(dú)占鎖
        lock.unlock();
    }
}

2??移除首節(jié)點(diǎn)的操作是通過(guò) unlinkFirst() 來(lái)完成的:

private E unlinkFirst() {
    // assert lock.isHeldByCurrentThread();
    // 獲取首節(jié)點(diǎn)
    Node<E> f = first;
    // 首節(jié)點(diǎn)為null,則返回null
    if (f == null)
        return null;
    // 獲取首節(jié)點(diǎn)的后繼節(jié)點(diǎn)
    Node<E> n = f.next;
    // 移除first砚尽,將首節(jié)點(diǎn)更新為n
    E item = f.item;
    f.item = null;
    f.next = f; // help GC
    first = n;
    // 移除首節(jié)點(diǎn)后施无,為空隊(duì)列
    if (n == null)
        last = null;
    else
        // 將新的首節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)設(shè)置為null
        n.prev = null;
    --count;
    // 喚醒阻塞在notFull上的線程
    notFull.signal();
    return item;
}

3??pollLast() 是獲取并移除此雙端隊(duì)列的尾節(jié)點(diǎn),若不存在必孤,則返回 null猾骡,源碼如下:

public E pollLast() {
    // 獲取全局獨(dú)占鎖
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        return unlinkLast();
    } finally {
        // 釋放全局獨(dú)占鎖
        lock.unlock();
    }
}

4??移除尾節(jié)點(diǎn)的操作是通過(guò) unlinkLast() 來(lái)完成的:

private E unlinkLast() {
    // assert lock.isHeldByCurrentThread();
    // 獲取尾節(jié)點(diǎn)
    Node<E> l = last;
    // 尾節(jié)點(diǎn)為null,則返回null
    if (l == null)
        return null;
    // 獲取尾節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)
    Node<E> p = l.prev;
    // 移除尾節(jié)點(diǎn),將尾節(jié)點(diǎn)更新為p
    E item = l.item;
    l.item = null;
    l.prev = l; // help GC
    last = p;
    // 移除尾節(jié)點(diǎn)后兴想,為空隊(duì)列
    if (p == null)
        first = null;
    else
        // 將新的尾節(jié)點(diǎn)的后繼節(jié)點(diǎn)設(shè)置為null
        p.next = null;
    --count;
    // 喚醒阻塞在notFull上的線程
    notFull.signal();
    return item;
}

其實(shí) LinkedBlockingDeque 類的入隊(duì)幢哨、出隊(duì)操作都是通過(guò) linkFirst、linkLast襟企、unlinkFirst嘱么、unlinkLast 這幾個(gè)方法來(lái)實(shí)現(xiàn)的,源碼讀起來(lái)也比較簡(jiǎn)單顽悼。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末曼振,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蔚龙,更是在濱河造成了極大的恐慌冰评,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,464評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件木羹,死亡現(xiàn)場(chǎng)離奇詭異甲雅,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)坑填,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門抛人,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人脐瑰,你說(shuō)我怎么就攤上這事妖枚。” “怎么了苍在?”我有些...
    開封第一講書人閱讀 169,078評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵绝页,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我寂恬,道長(zhǎng)续誉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,979評(píng)論 1 299
  • 正文 為了忘掉前任初肉,我火速辦了婚禮酷鸦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘牙咏。我一直安慰自己臼隔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,001評(píng)論 6 398
  • 文/花漫 我一把揭開白布眠寿。 她就那樣靜靜地躺著,像睡著了一般焦蘑。 火紅的嫁衣襯著肌膚如雪盯拱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,584評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音狡逢,去河邊找鬼宁舰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛奢浑,可吹牛的內(nèi)容都是我干的蛮艰。 我是一名探鬼主播,決...
    沈念sama閱讀 41,085評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼雀彼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼壤蚜!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起徊哑,我...
    開封第一講書人閱讀 40,023評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤袜刷,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后莺丑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體著蟹,經(jīng)...
    沈念sama閱讀 46,555評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,626評(píng)論 3 342
  • 正文 我和宋清朗相戀三年梢莽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了萧豆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,769評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡昏名,死狀恐怖涮雷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情葡粒,我是刑警寧澤份殿,帶...
    沈念sama閱讀 36,439評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站嗽交,受9級(jí)特大地震影響卿嘲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜夫壁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,115評(píng)論 3 335
  • 文/蒙蒙 一拾枣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盒让,春花似錦梅肤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至肺缕,卻和暖如春左医,著一層夾襖步出監(jiān)牢的瞬間授帕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工浮梢, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留跛十,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,191評(píng)論 3 378
  • 正文 我出身青樓秕硝,卻偏偏與公主長(zhǎng)得像芥映,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子远豺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,781評(píng)論 2 361