非阻塞算法(Lock-Free)的實現java
上篇文章咱們講到了使用鎖會帶來的各類缺點,本文將會講解如何使用非阻塞算法。非阻塞算法通常會使用CAS來協調線程的操做。git
雖然非阻塞算法有諸多優勢,可是在實現上要比基於鎖的算法更加繁瑣和負責。github
本文將會介紹兩個是用非阻塞算法實現的數據結構。算法
咱們先使用CAS來構建幾個非阻塞的棧。棧是最簡單的鏈式結構,其本質是一個鏈表,而鏈表的根節點就是棧頂。數據結構
咱們先構建Node數據結構:this
public class Node<E> {
public final E item;
public Node<E> next;
public Node(E item){
this.item=item;
}
}
複製代碼
這個Node保存了內存item和它的下一個節點next。spa
而後咱們構建非阻塞的棧,在該棧中咱們須要實現pop和push方法,咱們使用一個Atomic類來保存top節點的引用,在pop和push以前調用compareAndSet命令來保證命令的原子性。同時,咱們須要不斷的循環,以保證在線程衝突的時候可以重試更新。線程
public class ConcurrentStack<E> {
AtomicReference<Node<E>> top= new AtomicReference<>();
public void push(E item){
Node<E> newNode= new Node<>(item);
Node<E> oldNode;
do{
oldNode=top.get();
newNode.next= oldNode;
}while(!top.compareAndSet(oldNode, newNode));
}
public E pop(){
Node<E> oldNode;
Node<E> newNode;
do {
oldNode = top.get();
if(oldNode == null){
return null;
}
newNode=oldNode.next;
}while(!top.compareAndSet(oldNode, newNode));
return oldNode.item;
}
}
複製代碼
構建鏈表要比構建棧複雜。由於咱們要維持頭尾兩個指針。以put方法來講,咱們須要執行兩步操做:1. 在尾部插入新的節點。2.將尾部指針指向最新的節點。指針
咱們使用CAS最多隻能保證其中的一步是原子執行。那麼對於1和2的組合步驟該怎麼處理呢?code
咱們再仔細考慮考慮,其實1和2並不必定要在同一個線程中執行,其餘線程在檢測到有線程插入了節點,可是沒有將tail指向最後的節點時,徹底幫忙完成這個操做。
咱們看下具體的代碼實現:
public class LinkedNode<E> {
public final E item;
public final AtomicReference<LinkedNode<E>> next;
public LinkedNode(E item, LinkedNode<E> next){
this.item=item;
this.next=new AtomicReference<>(next);
}
}
複製代碼
先構建一個LinkedNode類。
public class LinkedQueue<E> {
private final LinkedNode<E> nullNode= new LinkedNode<>(null, null);
private final AtomicReference<LinkedNode<E>> head= new AtomicReference<>(nullNode);
private final AtomicReference<LinkedNode<E>> tail= new AtomicReference<>(nullNode);
public boolean put(E item){
LinkedNode<E> newNode = new LinkedNode<>(item, null);
while (true){
LinkedNode<E> currentTail= tail.get();
LinkedNode<E> tailNext= currentTail.next.get();
if(currentTail == tail.get()){
if (tailNext != null) {
//有其餘的線程已經插入了一個節點,可是尚未將tail指向最新的節點
tail.compareAndSet(currentTail, tailNext);
}else{
//沒有其餘的線程插入節點,那麼作兩件事情:1. 插入新節點,2.將tail指向最新的節點
if(currentTail.next.compareAndSet(null, newNode)){
tail.compareAndSet(currentTail, newNode);
}
}
}
}
}
}
複製代碼
本文的例子能夠參考github.com/ddean2009/l…
更多內容請訪問 www.flydean.com/java-lock-f…