由於鍊錶的特性,因此可以知道其進行新增元素或者刪除元素只需要進行節點的改變即可,同時不需要考慮擴容和縮容的問題,比較方便。那實現佇列,需要的是生產者和消費者的模型。其本質是執行進隊和出隊操作。 下面的**源於
那麼怎樣才能實現乙個併發無鎖的佇列呢?首先需要考慮佇列是基於鍊錶的,因此我們能操作它的前驅節點和後繼節點,同時對資料需要儲存到佇列中,然後進行消費。為了解決併發問題,我們採用樂觀鎖+volatile的方式實現。為了測試效果,**中使用了同步併發容器countdownlatch測試。
入隊考慮:
當前隊列為空時,直接將當前節點設定成尾節點
當前佇列不為空時,將原來的尾節點設定未當前節點的前驅節點
出隊考慮:
如果當前隊列為空,則將返回空
如果當前佇列不為空,有乙個元素,則直接移除,將當前頭節點設定為空
如果當前佇列有多個元素,此時考慮將頭節點進行置為空,此時首先拿到頭節點的下乙個節點,同時將其的前驅節點(也即頭節點)設定為空
因此它的資料結構:
前驅節點、後繼節點、當前佇列儲存的資料
/**
* 鍊錶節點的定義
* * @param */
private
static
class
node
@override
public string tostring()
';}}
由於我們的資料儲存時,需要有先後位置,此時需要考慮到佇列有隊頭和隊尾,因此需要將其定義出來:
// 指向佇列頭結點的原子引用
private atomicreference
> head =
newatomicreference
<
>
(null)
;// 指向佇列尾結點的原子引用
private atomicreference
> tail =
newatomicreference
<
>
(null)
;
實現佇列的入隊操作,如果當前的隊列為空,則首先獲取隊尾的值,執行cas比較,null與當前隊尾的值比較,然後執行隊列入隊操作,如果不為空,則首先將原來的隊尾的資料賦給當前節點的前驅節點,同時比較隊尾節點節點和當前節點,再進行更新:
public
boolean
inqueue
(e e)
}else}}
}
進行出隊操作:首先如果當前隊列為空,則直接返回空,如果當前的佇列不為空,則檢視當前的頭尾節點是否相等,如果相等,則資料只有乙個資料,直接將頭設定為空,否則說明有多個資料,此時直接進行前移操作
public e dequeue()
else
if(headed == tailed)
}else}}
}
使用併發容器countdownlatch進行測試:
private
long
singlelinkedqueue()
throws interruptedexception
else
} latch.
countdown()
;}})
.start()
;}latch.
await()
;long end = system.
currenttimemillis()
;return
(end - start)
;}
基於鍊錶實現無鎖佇列或者基於棧實現無鎖併發棧的本質是類似的。
但是這樣的無鎖佇列適合大容量場景嗎?
對於無鎖佇列的鍊錶模式:
無鎖佇列的鍊錶模式採用head/tail頭尾指標指向乙個包含next指標的結構體。
為了解決head/tail的互斥問題,head/tail必須指向乙個dummy結構(空佇列時head=tail 指向dummy)
雙向佇列的鍊錶實現
定義以下這樣乙個佇列結構 include include define elementtype int define error 1e5 typedef struct node ptrtonode struct node typedef struct dequerecord deque struct...
基於雙向鍊錶的簡單阻塞佇列
最近工作比較忙,水一篇 author niuxy date 2020 6 8 8 54 下午 description 基於雙向鍊錶的簡單 fifo 佇列 鎖的粒度很粗,僅針對最上層操作進行互斥同步關係規劃 最上層方法有兩個 put 與 remove 以 length 與 capacity 是否相等為...
無頭雙向鍊錶
class node public class mylinkedlist else 尾插法 public void addlast int data else private void checkindex int index private node searchindex int index r...