JDK并發(fā)工具類源碼--ConcurrentLinkedQueue

實(shí)現(xiàn)并發(fā)安全有兩種方式:一種是阻塞式的:例如:LinkedBlockingQueue;另一種是非阻塞式的:例如:ConcurrentLinkedQueue,非阻塞式的最顯著的優(yōu)點(diǎn)是性能,非阻塞式算法使用CAS原子性來更新數(shù)據(jù)忠藤。避免了加鎖的時(shí)間,同時(shí)也保證了數(shù)據(jù)的一致性。

1.ConcurrentLinkedQueue簡介

ConcurrentLinkedQueue中包含兩個(gè)內(nèi)部類席舍,Node<E>和Itr稼虎。Node<E>表示ConcurrentLinkedQueue鏈表中的一個(gè)節(jié)點(diǎn)孔飒;Itr類用來遍歷ConcurrentLinkedQueue。

2.常用方法

2.1offer()

public boolean offer(E e) {
    if (e == null) throw new NullPointerException();
    //入隊(duì)前骤公,創(chuàng)建一個(gè)入隊(duì)節(jié)點(diǎn)
    Node<E> n = new Node<E>(e);
    retry:
    //死循環(huán),入隊(duì)不成功反復(fù)入隊(duì)扬跋。
    for (;;) {
        //創(chuàng)建一個(gè)指向tail節(jié)點(diǎn)的引用
        Node<E> t = tail;
        //p用來表示隊(duì)列的尾節(jié)點(diǎn)阶捆,默認(rèn)情況下等于tail節(jié)點(diǎn)。
        Node<E> p = t;
        for (int hops = 0; ; hops++) {
        //獲得p節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)钦听。
            Node<E> next = succ(p);
        //next節(jié)點(diǎn)不為空洒试,說明p不是尾節(jié)點(diǎn),需要更新p后在將它指向next節(jié)點(diǎn)
            if (next != null) {
               //循環(huán)了兩次及其以上朴上,并且當(dāng)前節(jié)點(diǎn)還是不等于尾節(jié)點(diǎn)
                if (hops > HOPS && t != tail)
                    continue retry; 
                p = next;
            } 
            //如果p是尾節(jié)點(diǎn)垒棋,則設(shè)置p節(jié)點(diǎn)的next節(jié)點(diǎn)為入隊(duì)節(jié)點(diǎn)。
            else if (p.casNext(null, n)) {
              //如果tail節(jié)點(diǎn)有大于等于1個(gè)next節(jié)點(diǎn)痪宰,則將入隊(duì)節(jié)點(diǎn)設(shè)置成tair節(jié)點(diǎn)叼架,更新失敗了也
沒關(guān)系,因?yàn)槭×吮硎居衅渌€程成功更新了tair節(jié)點(diǎn)衣撬。
                if (hops >= HOPS)
                    casTail(t, n); // 更新tail節(jié)點(diǎn)乖订,允許失敗
                return true;  
            } 
           // p有next節(jié)點(diǎn),表示p的next節(jié)點(diǎn)是尾節(jié)點(diǎn),則重新設(shè)置p節(jié)點(diǎn)
            else {
                p = succ(p);
            }
        }
    }
}

上面代碼的設(shè)計(jì)原理有兩點(diǎn)具练,1.定位出尾節(jié)點(diǎn)垢粮;2.使用CAS將入隊(duì)節(jié)點(diǎn)設(shè)置成尾節(jié)點(diǎn)的next節(jié)點(diǎn)。

2.2poll

public E poll() {
    Node<E> h = head;
   // p表示頭節(jié)點(diǎn)靠粪,需要出隊(duì)的節(jié)點(diǎn)
    Node<E> p = h;
    for (int hops = 0;; hops++) {
        // 獲取p節(jié)點(diǎn)的元素
        E item = p.getItem();
        // 如果p節(jié)點(diǎn)的元素不為空蜡吧,使用CAS設(shè)置p節(jié)點(diǎn)引用的元素為null,如果成功則返回p節(jié)點(diǎn)的元素。
        if (item != null && p.casItem(item, null)) {
            if (hops >= HOPS) {
                //將p節(jié)點(diǎn)下一個(gè)節(jié)點(diǎn)設(shè)置成head節(jié)點(diǎn)
                Node<E> q = p.getNext();
                updateHead(h, (q != null) ? q : p);
            }
            return item;
        }
        // 如果頭節(jié)點(diǎn)的元素為空或頭節(jié)點(diǎn)發(fā)生了變化占键,這說明頭節(jié)點(diǎn)已經(jīng)被另外一個(gè)線程修改了昔善。那么獲取p節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn) 
        Node<> next = succ(p);
        // 如果p的下一個(gè)節(jié)點(diǎn)也為空,說明這個(gè)隊(duì)列已經(jīng)空了
        if (next == null) {
          // 更新頭節(jié)點(diǎn)畔乙。
            updateHead(h, p);
            break;
        }
        // 如果下一個(gè)元素不為空君仆,則將頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)設(shè)置成頭節(jié)點(diǎn)
        p = next;
    }
    return null;
}

首先獲取頭結(jié)點(diǎn)的值,然后判斷頭結(jié)點(diǎn)元素是否為空,如果為空返咱,就表示另一個(gè)線程已經(jīng)進(jìn)行了一次出隊(duì)的操作钥庇,如果不為空,則使用CAS將頭結(jié)點(diǎn)的引用設(shè)置為空咖摹,如果CAS成功评姨,直接返回頭結(jié)點(diǎn)的元素,如果不成功萤晴,另外一個(gè)線程已經(jīng)完成了出隊(duì)操作并跟新了head元素吐句,,需要重新獲取頭結(jié)點(diǎn)店读。

2.3remove

public boolean remove(Object o) {  
        if (o == null) return false;  
        Node<E> pred = null;  
        // 這里是從head 開始的嗦枢,中間還涉及到head 的判斷等從操作  
        // 跟一般for 循環(huán)類似,要遍歷的- -屯断,這樣的操作o 很靠后的時(shí)候文虏,會(huì)慢- -!  
        // - -不太喜歡這方法殖演,了解作用  
        for (Node<E> p = first(); p != null; p = succ(p)) {  
            E item = p.item;  
            if (item != null &&  
                    o.equals(item) &&  
                    p.casItem(item, null)) {  
                Node<E> next = succ(p);  
                if (pred != null && next != null)  
                    pred.casNext(p, next);  
                return true;  
            }  
            pred = p;  
        }  
        return false;  
    } 
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末择葡,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子剃氧,更是在濱河造成了極大的恐慌敏储,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件朋鞍,死亡現(xiàn)場離奇詭異已添,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)滥酥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門更舞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坎吻,你說我怎么就攤上這事缆蝉。” “怎么了瘦真?”我有些...
    開封第一講書人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵刊头,是天一觀的道長。 經(jīng)常有香客問我诸尽,道長原杂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任您机,我火速辦了婚禮穿肄,結(jié)果婚禮上年局,老公的妹妹穿的比我還像新娘。我一直安慰自己咸产,他們只是感情好矢否,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著脑溢,像睡著了一般僵朗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上焚志,一...
    開封第一講書人閱讀 51,604評(píng)論 1 305
  • 那天衣迷,我揣著相機(jī)與錄音畏鼓,去河邊找鬼酱酬。 笑死,一個(gè)胖子當(dāng)著我的面吹牛云矫,可吹牛的內(nèi)容都是我干的膳沽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼让禀,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼挑社!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起巡揍,我...
    開封第一講書人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤痛阻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后腮敌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阱当,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年糜工,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了弊添。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡捌木,死狀恐怖油坝,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情刨裆,我是刑警寧澤澈圈,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站帆啃,受9級(jí)特大地震影響极舔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜链瓦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一拆魏、第九天 我趴在偏房一處隱蔽的房頂上張望盯桦。 院中可真熱鬧,春花似錦渤刃、人聲如沸拥峦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽略号。三九已至,卻和暖如春洋闽,著一層夾襖步出監(jiān)牢的瞬間玄柠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來泰國打工诫舅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留羽利,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓刊懈,卻偏偏與公主長得像这弧,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子虚汛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

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

  • 相關(guān)文章Java并發(fā)編程(一)線程定義匾浪、狀態(tài)和屬性 Java并發(fā)編程(二)同步Java并發(fā)編程(三)volatil...
    劉望舒閱讀 2,505評(píng)論 2 14
  • Java程序員進(jìn)行并發(fā)編程時(shí),相比于其他語言的程序員而言要倍感幸福卷哩,因?yàn)椴l(fā)編程大師Doug Lea不遺余力地為J...
    luoxn28閱讀 452評(píng)論 0 0
  • “請(qǐng)讓我吃掉你蛋辈。” 波比可不是一只隨便的龍将谊,他不是經(jīng)常會(huì)覺得餓冷溶,也不是對(duì)誰都說同樣的話。 自從跟著鮭魚們從海里一路...
    柴林2閱讀 344評(píng)論 0 3
  • 人生就是一個(gè)逐漸醒悟的過程瓢娜,從出生到死亡挂洛,會(huì)悟出許多人生的真諦,領(lǐng)悟到所有后眠砾,人生也該到結(jié)束的時(shí)候了虏劲。
    醒悟的蝸牛閱讀 153評(píng)論 0 0
  • 我盤坐在碧綠色的山巔 靜聽山風(fēng)擊落沙石 遠(yuǎn)視著五六層的棕紅 心里是漫天的稀稀落落 數(shù)不盡的星點(diǎn)兒 我的熱烈如火 我...
    東樓飄雪閱讀 256評(píng)論 1 7