摘要:是無(wú)阻塞隊(duì)列的一種實(shí)現(xiàn),依賴與算法實(shí)現(xiàn)。這樣比從往后找更有效率出隊(duì)規(guī)則定義補(bǔ)充一項(xiàng),也表示節(jié)點(diǎn)已經(jīng)被刪除參考方法。
ConcurrentLinkedQueue是無(wú)阻塞隊(duì)列的一種實(shí)現(xiàn), 依賴與CAS算法實(shí)現(xiàn)。
入隊(duì)offerif(q==null)當(dāng)前是尾節(jié)點(diǎn) -> CAS賦值tail.next = newNode, 成功就跳出循環(huán)
elseif(p == q)尾節(jié)點(diǎn)被移除 -> 從tail或head重新往后找
else不是尾節(jié)點(diǎn) -> 往next找
規(guī)則定義:當(dāng)一個(gè)節(jié)點(diǎn)的next指向自身時(shí), 表示節(jié)點(diǎn)已經(jīng)被移除, 注釋中還會(huì)強(qiáng)調(diào)這一點(diǎn)。
完整代碼(JDK8):/** * Inserts the specified element at the tail of this queue. * As the queue is unbounded, this method will never return {@code false}. * * @return {@code true} (as specified by {@link Queue#offer}) * @throws NullPointerException if the specified element is null */ /* * 變量說(shuō)明: * 成員變量: * head: 首節(jié)點(diǎn) * tail: 尾節(jié)點(diǎn), 不一定指向末尾, 兩次入隊(duì)才更新一次 * 局部變量 * t= tail; //保存循環(huán)開(kāi)始時(shí), 當(dāng)時(shí)的tail值 * p= t; // 每次查找的起始位置, 可能指向首節(jié)點(diǎn)head或者臨時(shí)尾節(jié)點(diǎn)t * q= p.next; // 每次循環(huán)下一個(gè)節(jié)點(diǎn) * newNode= new Node; // 新節(jié)點(diǎn) * * * 重要概念: * 當(dāng)p = p.next時(shí), 表示節(jié)點(diǎn)已經(jīng)被移除 */ public boolean offer(E e) { checkNotNull(e); final Node這兩段代碼看了很久, 特別記錄下:newNode = new Node (e); for (Node t = tail, p = t;;) { Node q = p.next; if (q == null) { // 情況1: p是尾節(jié)點(diǎn) // p is last node // p是尾節(jié)點(diǎn)就直接將新節(jié)點(diǎn)放入末尾 if (p.casNext(null, newNode)) { // Successful CAS is the linearization point // for e to become an element of this queue, // and for newNode to become "live". if (p != t) // hop two nodes at a time // 一次跳兩個(gè)節(jié)點(diǎn), 即插入兩次, tail更新一次 casTail(t, newNode); // Failure is OK. // 失敗也無(wú)妨, 說(shuō)明被別的線程更新了 return true; // 退出循環(huán) } // Lost CAS race to another thread; re-read next } else if (p == q) // 情況2: p節(jié)點(diǎn)被刪除了 // We have fallen off list. If tail is unchanged, it // will also be off-list, in which case we need to // jump to head, from which all live nodes are always // reachable. Else the new tail is a better bet. // 保存的節(jié)點(diǎn)p、t都已經(jīng)失效了,這時(shí)需要重新檢索,重新檢索的起始位置有兩種情況 // 1.1. 如果tail==t,表示tail也是失效的, 那么從head開(kāi)始找 // 1.2. 否則tail就是被其他線程更新了, 可以又試著從tail找 p = (t != (t = tail)) ? t : head; else // 情況3: 沿著p往下找 // Check for tail updates after two hops. // 這段簡(jiǎn)單看作p = q就好理解了, 這么寫(xiě)是為了提高效率: // 1. 情況二中p可能指向了head(由于tail節(jié)點(diǎn)失效導(dǎo)致的) // 2. 現(xiàn)在tail可能被其他線程更新,也許重新指向了隊(duì)尾 // 3. 如果是, 嘗試則從隊(duì)尾開(kāi)始找, 以減少迭代次數(shù) p = (p != t && t != (t = tail)) ? t : q; } }
情況2中的p = (t != (t = tail)) ? t : head;
(t != (t = tail))可以分三步來(lái)看
1.1. 首先取出t 1.2. 將tail賦值給t 1.3. 將先前取出的t與更新后的t比較
情況3中p = (p != t && t != (t = tail)) ? t : q;
首先: p != t: 這種情況只有可能發(fā)生在執(zhí)行了情況2后出隊(duì)poll 規(guī)則定義:
現(xiàn)狀: 這時(shí)p指向head或者中間的元素, t指向一個(gè)被刪除了的節(jié)點(diǎn)
那么如果tail被其他線程更新了, 我們可以將t重新指向tail, p指向t, 就像剛進(jìn)循環(huán)一樣, 從尾節(jié)點(diǎn)開(kāi)始檢索。
這樣比從head往后找更有效率
補(bǔ)充一項(xiàng), item==null,也表示節(jié)點(diǎn)已經(jīng)被刪除(參考remove方法)。
/** * updateHead * */ public E poll() { restartFromHead: for (;;) { for (Node出隊(duì)設(shè)值操作:h = head, p = h, q;;) { E item = p.item; if (item != null && p.casItem(item, null)) { // Successful CAS is the linearization point // for item to be removed from this queue. if (p != h) // hop two nodes at a time 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; } } } /** * Tries to CAS head to p. If successful, repoint old head to itself * as sentinel for succ(), below. */ final void updateHead(Node h, Node p) { if (h != p && casHead(h, p)) h.lazySetNext(h); }
先更新head, 再將舊head的next指向自己
Note: CAS算法實(shí)現(xiàn)依靠Unsafe.compareAndSwapObject實(shí)現(xiàn)UNSAFE.compareAndSwapObject(對(duì)象, 字段偏移量, 當(dāng)前值, 新值)
可以為對(duì)象中的某個(gè)字段實(shí)現(xiàn)CAS操作lazySet依賴UNSAFE.putOrderedObject實(shí)現(xiàn)
UNSAFE.putOrderedObject(對(duì)象, 字段偏移量, 新值)
這個(gè)只能用在volatile字段上
個(gè)人理解: volatile的設(shè)值會(huì)導(dǎo)致本地緩存失效, 那么需要重新從主存讀取, 使用這個(gè)方法可以使寄存器緩存依舊有效, 不必急于從主存取值。
使用目的: 移除節(jié)點(diǎn)時(shí), 需要更新節(jié)點(diǎn)的next指向自身, 但現(xiàn)在next指向的數(shù)據(jù)實(shí)際是有效的; 高并發(fā)情況下,如果offser方法已經(jīng)緩存了這個(gè)next值, 直接設(shè)置next會(huì)導(dǎo)致緩存行失效, CPU需要重新讀取next; 而使用putOrderedObject可以讓offser從這個(gè)next繼續(xù)檢索
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/72604.html
摘要:注意這里指的不是當(dāng)次而是之后,所以如果我們使用隊(duì)列的方法返回,就知道隊(duì)列是否為空,但是不知道之后是否為空,并且,當(dāng)關(guān)注的操作發(fā)生時(shí),在插入或取出操作的返回值里告知此信息,來(lái)指導(dǎo)是否繼續(xù)注冊(cè)寫(xiě)操作。 前言 本文寫(xiě)給對(duì)ConcurrentLinkedQueue的實(shí)現(xiàn)和非阻塞同步算法的實(shí)現(xiàn)原理有一定了解,但缺少實(shí)踐經(jīng)驗(yàn)的朋友,文中包括了實(shí)戰(zhàn)中的嘗試、所走的彎路,經(jīng)驗(yàn)和教訓(xùn)。 背景介紹 ...
摘要:線程安全的線程安全的,在讀多寫(xiě)少的場(chǎng)合性能非常好,遠(yuǎn)遠(yuǎn)好于高效的并發(fā)隊(duì)列,使用鏈表實(shí)現(xiàn)。這樣帶來(lái)的好處是在高并發(fā)的情況下,你會(huì)需要一個(gè)全局鎖來(lái)保證整個(gè)平衡樹(shù)的線程安全。 該文已加入開(kāi)源項(xiàng)目:JavaGuide(一份涵蓋大部分Java程序員所需要掌握的核心知識(shí)的文檔類項(xiàng)目,Star 數(shù)接近 14 k)。地址:https://github.com/Snailclimb... 一 JDK ...
摘要:如果停止了版本更新,可使用方法來(lái)解除所有因而阻塞的線程,包括指定版本號(hào)的。如果自己維護(hù)版本號(hào),則應(yīng)該保證遞增。 前言 相比上一篇而言,本文不需要太多的準(zhǔn)備知識(shí),但技巧性更強(qiáng)一些。因?yàn)榉治?、設(shè)計(jì)的過(guò)程比較復(fù)雜繁瑣,也限于篇幅,所以,主要展示如何解決這些需求,和講解代碼。另外,所講的內(nèi)容也是后一篇實(shí)戰(zhàn)中需要用到的一個(gè)工具類。 需求介紹 我需要編寫(xiě)一個(gè)同步工具,它需要提供這樣幾個(gè)方法:...
摘要:對(duì)象改變條件對(duì)象當(dāng)前線程要等待線程終止之后才能從返回。如果線程在上的操作中被中斷,通道會(huì)被關(guān)閉,線程的中斷狀態(tài)會(huì)被設(shè)置,并得到一個(gè)。清除線程的中斷狀態(tài)。非公平性鎖雖然可能造成饑餓,但極少的線程切換,保證其更大的吞吐量。 聲明:Java并發(fā)的內(nèi)容是自己閱讀《Java并發(fā)編程實(shí)戰(zhàn)》和《Java并發(fā)編程的藝術(shù)》整理來(lái)的。 showImg(https://segmentfault.com/im...
摘要:序是一個(gè)基于鏈接節(jié)點(diǎn)的無(wú)界線程安全隊(duì)列,它采用先進(jìn)先出的規(guī)則對(duì)節(jié)點(diǎn)進(jìn)行排序,當(dāng)我們添加一個(gè)元素的時(shí)候,它會(huì)添加到隊(duì)列的尾部,當(dāng)我們獲取一個(gè)元素時(shí),它會(huì)返回隊(duì)列頭部的元素。的模型,默認(rèn)的是用這個(gè)來(lái)實(shí)現(xiàn)的。 序 ConcurrentLinkedQueue是一個(gè)基于鏈接節(jié)點(diǎn)的無(wú)界線程安全隊(duì)列,它采用先進(jìn)先出的規(guī)則對(duì)節(jié)點(diǎn)進(jìn)行排序,當(dāng)我們添加一個(gè)元素的時(shí)候,它會(huì)添加到隊(duì)列的尾部,當(dāng)我們獲取一個(gè)元...
閱讀 1848·2021-11-11 16:55
閱讀 1462·2019-08-30 15:54
閱讀 783·2019-08-29 15:34
閱讀 2263·2019-08-29 13:11
閱讀 2919·2019-08-26 13:28
閱讀 1886·2019-08-26 10:49
閱讀 1003·2019-08-26 10:40
閱讀 2564·2019-08-23 18:21