1. 程式人生 > >基於CAS的非阻塞同步佇列的實現(一)

基於CAS的非阻塞同步佇列的實現(一)

基於CAS實現的元件有很多,這裡選擇非阻塞同步佇列的原因是,當有多個執行緒同時新增元素時,在佇列的尾部如何利用cas保證兩個操作的原子性是有相應演算法的(這兩個步驟是尾節點需要指向新增的元素,最後一個節點元素需要指向新增的元素,即新增的元素必須同時有這兩個節點指向它)。程式碼如下:

public class ConcurentQueue {

    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;
                }
            }
        }
    }
}

打*的兩個地方原本是需要同時成功才能保證正確性的,但根據現在瞭解的是cas只能保證一個操作的原子性,若要保證兩個操作的原子性,初看是不可能的。細心的朋友可以看出這些程式碼和java併發程式設計實戰中的是幾乎一樣的,也只有將head節點也作為啞節點這一小小改動,(注意尾節點和最後一個節點是不同的節點),首先佇列永遠只有兩種狀態,見書中的原圖(不過描述得改成中間態和穩定態,其餘的不要看尤其是那個”對立“讓人看不懂):

其次佇列永遠只能在最後一個節點後面即穩定態的時候才能插入!

先分析一下程式碼:

1、當B步驟成功後,還沒來得及執行C步驟前,此時佇列就處於中間態,其餘執行緒就會執行A步驟將尾節點推到最後一個節點上從而形成穩定態,此時再執行C步驟時儘管失敗了但是佇列已經穩定;

2、只要佇列不穩定時,所有的執行緒都會設法執行A步驟將佇列趨於穩定;

3、C步驟不管是成功還是失敗佇列都會處於穩定狀態;

到這裡大家可以去體會一下java併發程式設計實戰中說的:那兩個技巧了,說實在的沒看懂程式碼前能看懂那些話麼....,還有能想到這種方法確實不容易,想想還是用鎖實現比較簡單明瞭。