JUC併發容器——ConcurrentLinkedQueue

線程安全的隊列

  • 阻塞隊列:使用一個鎖(進隊和出隊同一個鎖)和兩個鎖(入隊和出隊用不同的鎖)
  • 非阻塞隊列:ConcurrentLinkedQueue

ConcurrentLinkedQueue特性:

  1. 是一個基於鏈接節點的無界線程安全隊列,
  2. 先進先出的插入順序進行排序,
  3. 採用“wait-free”算法(CAS算法)實現,
  4. 具體實現:添加元素時它會添加到隊列尾部;獲取元素時,它會返回隊列頭部的元素。

1.8 source code

https://blog.csdn.net/chenssy/article/details/74853120
1、入隊方法

public boolean offer(E e) {
    //判斷入隊節點是否爲空
    checkNotNull(e);
    //構造入隊節點
    final Node<E> newNode = new Node<E>(e);
    //死循環,入隊不成功繼續入隊
    //p初始爲tail
    for (Node<E> t = tail, p = t;;) {
        //q爲p的後繼,若p爲隊尾,則q爲空,若p不是隊尾,則有其他線程更改了隊列
        Node<E> q = p.next;
        if (q == null) {
            //p是隊尾  將入隊節點變成隊尾的後繼
            if (p.casNext(null, newNode)) {
                //判斷tail節點是否爲尾節點
                if (p != t)
                    //如果不是,將入隊節點修改爲隊尾
                    casTail(t, newNode);
                return true;
            }
            // Lost CAS race to another thread; re-read next
        }
        //說明這個節點已經被移除了
        else if (p == q)
            //如果隊尾未發生改變,t還是隊尾,則將p指向t,繼續插入新節點
            //如果隊尾發生改變,則將p指向隊頭重新開始插入新節點
            p = (t != (t = tail)) ? t : head;
        else
            //說明p有後繼,p的後繼纔是隊尾,則需要將p指向隊尾
            p = (p != t && t != (t = tail)) ? t : q;
    }
}
入隊方法個人感覺還是挺複雜的。大致步驟如下

1、構造新節點

2、開始循環,p表示隊尾,默認p初始爲tail,

3、如果p後繼爲空,說明p就是tail,則插入新節點返回

4、如果p節點被刪除,則需要重新賦值p,如果tail未變,則將p賦值爲t,如果tail改變,則將p賦值爲隊頭,繼續插入

5、如果p有後繼,說明p不是tail,則需要將p指向tail。

2出隊方法

public E poll() {
    restartFromHead:
    //死循環
    for (;;) {
        for (Node<E> h = head, p = h, q;;) {
            //取出隊頭的值
            E item = p.item;
            //如果隊頭值不爲空則將隊頭的值更新爲空
            if (item != null && p.casItem(item, null)) {
                //判斷p是否等於head,如果不等於則重新指向隊頭
                if (p != h)
                    updateHead(h, ((q = p.next) != null) ? q : p);
                return item;
            }
            //隊列爲空
            else if ((q = p.next) == null) {
                updateHead(h, p);
                return null;
            }
            //有其他線程將隊頭的值出隊了,則重新開始出隊
            else if (p == q)
                continue restartFromHead;
            else
                p = q;
        }
    }
}
出隊的大致步驟如下:

1、h等於head,p初始化爲head

2、取出隊頭的值

3、如果隊頭的值不爲空,則將隊頭的值置空,然後判斷head是否變化,如果變化重新尋找head,返回隊頭的值

4、如果隊列爲空,則需要重新尋找隊頭,返回null

5、如果有其他線程將隊頭的值出隊了,那麼重新從最新head開始出隊
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章