14. LinkedBlockingDeque

LinkedBlockingDeque類實現(xiàn)了BlockingDeque接口懒震。閱讀BlockingDeque文本以獲取有關(guān)的更多信息。

Deque來自“雙端隊列” 這個詞嗤详。Deque是一個隊列个扰,你可以在插入和刪除隊列兩端的元素。

LinkedBlockingDeque是一個Deque葱色,如果一個線程試圖從中獲取一個元素递宅,而隊列空的,不管線程從哪一端試圖獲取元素苍狰,都會被阻塞办龄。

以下是實例化和使用LinkedBlockingDeque的例子:

BlockingDeque<String> deque = new LinkedBlockingDeque<String>();

deque.addFirst("1");
deque.addLast("2");

String two = deque.takeLast();
String one = deque.takeFirst();

源碼

LinkedBlockingDequeLinkedBlockingQueue的實現(xiàn)大體上類似,區(qū)別在于LinkedBlockingDeque提供的操作更多淋昭。并且LinkedBlockingQueue內(nèi)置兩個鎖分別用于put和take操作俐填,而LinkedBlockingDeque只使用一個鎖控制所有操作。因為隊列能夠同時在頭尾進(jìn)行put和take操作翔忽,所以使用兩個鎖也需要將兩個鎖同時加鎖才能保證操作的同步性英融,不如只使用一個鎖的性能好。

同步節(jié)點相比LinkedBlockingQueue多了一個prev字段歇式。

static final class Node<E> {
    E item;

    Node<E> prev;

    Node<E> next;

    Node(E x) {
        item = x;
    }
}

增加操作

增加操作相比LinkedBlockingQueue只能在隊列尾部增加驶悟,它能在隊列的頭尾兩端都進(jìn)行增加操作。

public void addFirst(E e) {
    // 復(fù)用offer方法
    if (!offerFirst(e))
        throw new IllegalStateException("Deque full");
}

public void addLast(E e) {
    if (!offerLast(e))
        throw new IllegalStateException("Deque full");
}

public boolean offerFirst(E e) {
    if (e == null) throw new NullPointerException();
    // 構(gòu)造節(jié)點
    Node<E> node = new Node<E>(e);
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 插入到隊列頭部
        return linkFirst(node);
    } finally {
        lock.unlock();
    }
}

private boolean linkFirst(Node<E> node) {
    // assert lock.isHeldByCurrentThread();

    // 如果隊列已滿材失,返回false
    if (count >= capacity)
        return false;
    // 獲取頭節(jié)點撩银,將自己的 next字段指向頭節(jié)點,然后設(shè)置自己為頭節(jié)點
    Node<E> f = first;
    node.next = f;
    first = node;
    // 如果隊列為空豺憔,尾節(jié)點也指向自己
    if (last == null)
        last = node;
    else
        f.prev = node;
    ++count;
    // 喚醒等待獲取元素的線程
    notEmpty.signal();
    return true;
}

public boolean offerLast(E e) {
    if (e == null) throw new NullPointerException();
    Node<E> node = new Node<E>(e);
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 插入到隊列尾部
        return linkLast(node);
    } finally {
        lock.unlock();
    }
}

private boolean linkLast(Node<E> node) {
    // assert lock.isHeldByCurrentThread();
    
    // 如果隊列已滿额获,返回false
    if (count >= capacity)
        return false;
    // 將自己設(shè)置為尾節(jié)點
    Node<E> l = last;
    node.prev = l;
    last = node;
    // 如果隊列為空,頭節(jié)點也指向自己
    if (first == null)
        first = node;
    else
        l.next = node;
    ++count;
    // 喚醒等待獲取元素的線程
    notEmpty.signal();
    return true;
}

public void putFirst(E e) throws InterruptedException {
    if (e == null) throw new NullPointerException();
    Node<E> node = new Node<E>(e);
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 如果隊列已滿恭应,等待
        while (!linkFirst(node))
            notFull.await();
    } finally {
        lock.unlock();
    }
}

public void putLast(E e) throws InterruptedException {
    if (e == null) throw new NullPointerException();
    Node<E> node = new Node<E>(e);
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 如果隊列已滿抄邀,等待
        while (!linkLast(node))
            notFull.await();
    } finally {
        lock.unlock();
    }
}

public boolean offerFirst(E e, long timeout, TimeUnit unit)
    throws InterruptedException {
    if (e == null) throw new NullPointerException();
    Node<E> node = new Node<E>(e);
    // 計算超時時間
    long nanos = unit.toNanos(timeout);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        // 如果隊列已滿,超時等待
        while (!linkFirst(node)) {
            if (nanos <= 0L)
                return false;
            nanos = notFull.awaitNanos(nanos);
        }
        return true;
    } finally {
        lock.unlock();
    }
}

public boolean offerLast(E e, long timeout, TimeUnit unit)
    throws InterruptedException {
    if (e == null) throw new NullPointerException();
    Node<E> node = new Node<E>(e);
    long nanos = unit.toNanos(timeout);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (!linkLast(node)) {
            if (nanos <= 0L)
                return false;
            nanos = notFull.awaitNanos(nanos);
        }
        return true;
    } finally {
        lock.unlock();
    }
}

刪除操作

public E removeFirst() {
    // 復(fù)用poll操作
    E x = pollFirst();
    if (x == null) throw new NoSuchElementException();
    return x;
}

public E removeLast() {
    E x = pollLast();
    if (x == null) throw new NoSuchElementException();
    return x;
}

public E pollFirst() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 獲取頭節(jié)點的值昼榛,并刪除它
        return unlinkFirst();
    } finally {
        lock.unlock();
    }
}

private E unlinkFirst() {
    // assert lock.isHeldByCurrentThread();

    // 如果隊列為空境肾,返回null
    Node<E> f = first;
    if (f == null)
        return null;
    // 重置頭節(jié)點
    Node<E> n = f.next;
    E item = f.item;
    f.item = null;
    f.next = f; // help GC
    first = n;
    if (n == null)
        last = null;
    else
        n.prev = null;
    --count;
    // 喚醒等待插入的線程
    notFull.signal();
    return item;
}

public E pollLast() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        return unlinkLast();
    } finally {
        lock.unlock();
    }
}

private E unlinkLast() {
    // assert lock.isHeldByCurrentThread();
    Node<E> l = last;
    // 隊列為空,返回null
    if (l == null)
        return null;
    // 更新尾節(jié)點
    Node<E> p = l.prev;
    E item = l.item;
    l.item = null;
    l.prev = l; // help GC
    last = p;
    if (p == null)
        first = null;
    else
        p.next = null;
    --count;
    notFull.signal();
    return item;
}

public E takeFirst() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        E x;
        // 如果隊列為空胆屿,等待
        while ( (x = unlinkFirst()) == null)
            notEmpty.await();
        return x;
    } finally {
        lock.unlock();
    }
}

public E takeLast() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        E x;
        // 如果隊列為空奥喻,等待
        while ( (x = unlinkLast()) == null)
            notEmpty.await();
        return x;
    } finally {
        lock.unlock();
    }
}

public E pollFirst(long timeout, TimeUnit unit)
    throws InterruptedException {
    long nanos = unit.toNanos(timeout);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        E x;
        while ( (x = unlinkFirst()) == null) {
            if (nanos <= 0L)
                return null;
            nanos = notEmpty.awaitNanos(nanos);
        }
        return x;
    } finally {
        lock.unlock();
    }
}

public E pollLast(long timeout, TimeUnit unit)
    throws InterruptedException {
    long nanos = unit.toNanos(timeout);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        E x;
        while ( (x = unlinkLast()) == null) {
            if (nanos <= 0L)
                return null;
            nanos = notEmpty.awaitNanos(nanos);
        }
        return x;
    } finally {
        lock.unlock();
    }
}

訪問操作

public E getFirst() {
    // 復(fù)用peek方法
    E x = peekFirst();
    if (x == null) throw new NoSuchElementException();
    return x;
}

public E getLast() {
    E x = peekLast();
    if (x == null) throw new NoSuchElementException();
    return x;
}

public E peekFirst() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 如果隊列不為空,返回頭元素
        return (first == null) ? null : first.item;
    } finally {
        lock.unlock();
    }
}

public E peekLast() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 如果隊列不為空非迹,返回尾元素
        return (last == null) ? null : last.item;
    } finally {
        lock.unlock();
    }
}

BlockingQueue 方法

由于BlockingDeque繼承自BlockingQueue接口环鲤,所以需要實現(xiàn)BlockingQueue中的方法,具體只需要復(fù)用前面提到的方法即可憎兽。

public boolean add(E e) {
    addLast(e);
    return true;
}

public boolean offer(E e) {
    return offerLast(e);
}

public void put(E e) throws InterruptedException {
    putLast(e);
}

public boolean offer(E e, long timeout, TimeUnit unit)
    throws InterruptedException {
    return offerLast(e, timeout, unit);
}

public E remove() {
    return removeFirst();
}

public E poll() {
    return pollFirst();
}

public E take() throws InterruptedException {
    return takeFirst();
}

public E poll(long timeout, TimeUnit unit) throws InterruptedException {
    return pollFirst(timeout, unit);
}

public E element() {
    return getFirst();
}

public E peek() {
    return peekFirst();
}

核心要點

  1. 內(nèi)部使用一個雙向鏈表
  2. 可以在鏈表兩頭同時進(jìn)行put和take操作冷离,只能使用一個鎖
  3. 插入線程在執(zhí)行完操作后如果隊列未滿會喚醒其他等待插入的線程吵冒,同時隊列非空還會喚醒等待獲取元素的線程;take線程同理西剥。
  4. 迭代器與內(nèi)部的雙向鏈表保持弱一致性痹栖,調(diào)用remove(T)方法刪除一個元素后,不會解除其對下一個結(jié)點的next引用瞭空,否則迭代器將無法工作揪阿。
  5. 迭代器的forEachRemaining(Consumer<? super E> action)以64個元素為一批進(jìn)行操作
  6. forEach(Consumer<? super E> action)removeIf咆畏,removeAll南捂,retainAll都是64個元素為一批進(jìn)行操作
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市鳖眼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嚼摩,老刑警劉巖钦讳,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異枕面,居然都是意外死亡愿卒,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門潮秘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來琼开,“玉大人,你說我怎么就攤上這事枕荞」窈颍” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵躏精,是天一觀的道長渣刷。 經(jīng)常有香客問我,道長矗烛,這世上最難降的妖魔是什么辅柴? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮瞭吃,結(jié)果婚禮上碌嘀,老公的妹妹穿的比我還像新娘。我一直安慰自己歪架,他們只是感情好股冗,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著和蚪,像睡著了一般魁瞪。 火紅的嫁衣襯著肌膚如雪穆律。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天导俘,我揣著相機與錄音峦耘,去河邊找鬼。 笑死旅薄,一個胖子當(dāng)著我的面吹牛辅髓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播少梁,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼洛口,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了凯沪?” 一聲冷哼從身側(cè)響起第焰,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎妨马,沒想到半個月后挺举,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡烘跺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年湘纵,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滤淳。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡梧喷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出脖咐,到底是詐尸還是另有隱情铺敌,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布屁擅,位于F島的核電站适刀,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏煤蹭。R本人自食惡果不足惜笔喉,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望硝皂。 院中可真熱鬧常挚,春花似錦、人聲如沸稽物。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贝或。三九已至吼过,卻和暖如春锐秦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盗忱。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工酱床, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人趟佃。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓扇谣,卻偏偏與公主長得像,于是被迫代替她去往敵國和親闲昭。 傳聞我的和親對象是個殘疾皇子罐寨,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

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