基於CAS實現的組件有不少,這裏選擇非阻塞同步隊列的緣由是,當有多個線程同時添加元素時,在隊列的尾部如何利用cas保證兩個操做的原子性是有相應算法的(這兩個步驟是尾節點須要指向新增的元素,最後一個節點元素須要指向新增的元素,即新增的元素必須同時有這兩個節點指向它)。代碼以下:java
public class ConcurentQueue {node
static class Node {
public Object e;
public AtomicReference<Node> nextRef ;算法
public Node(Object e,Node next) {
super();
this.e = e;
this.nextRef = new AtomicReference<Node>(next);
}
}
private Node head = new Node(null,null);
private Node tail = new Node(null,head);編程
public void add(Object e) {
Node new_node = new Node(e,null);//待插入節點
while(true){
Node curLast = tail.nextRef.get();//當前最後節點
Node next = curLast.nextRef.get();//當前最後節點的next節點
if(next != null){//A 若是當前最後節點的next節點不爲空,說明尾節點須要指向next節點
tail.nextRef.compareAndSet(curLast, next);
}else{
if(curLast.nextRef.compareAndSet(null, new_node)){//* B 將當前最後節點的next節點設置爲新插入的節點
tail.nextRef.compareAndSet(curLast, new_node);//* C 將尾節點的next節點設置爲新插入的節點
return;
}
}
}
}併發
public Object remove() {
while (true) {
Node next = head.nextRef.get();
if (next == null) {
return null;
} else {
Node nextSetNode = next.nextRef.get();
if (head.nextRef.compareAndSet(next, nextSetNode)) {
return next.e;
}
}
}
}
}this
打*的兩個地方本來是須要同時成功才能保證正確性的,但根據如今瞭解的是cas只能保證一個操做的原子性,若要保證兩個操做的原子性,初看是不可能的。細心的朋友能夠看出這些代碼和java併發編程實戰中的是幾乎同樣的,也只有將head節點也做爲啞節點這一小小改動,(注意尾節點和最後一個節點是不一樣的節點),首先隊列永遠只有兩種狀態,見書中的原圖(不過描述得改爲中間態和穩定態):線程
其次隊列永遠只能在最後一個節點後面即穩定態的時候才能插入!blog
先分析一下代碼:隊列
一、當B步驟成功後,還沒來得及執行C步驟前,此時隊列就處於中間態,其他線程就會執行A步驟將尾節點推到最後一個節點上從而造成穩定態,此時再執行C步驟時儘管失敗了可是隊列已經穩定;rem
二、只要隊列不穩定時,全部的線程都會設法執行A步驟將隊列趨於穩定;
三、C步驟無論是成功仍是失敗隊列都會處於穩定狀態;
到這裏你們能夠去體會一下java併發編程實戰中說的:那兩個技巧了,說實在的沒看懂代碼前能看懂那些話麼....,還有能想到這種方法確實不容易,想一想仍是用鎖實現比較簡單明瞭。