在java併發編程裏要實現原子操做,你們都會想到同步(synchronize),這種同步會使線程左塞,在性能上不是很滿意。基於CAS(Compare And Set) 能夠實現非左塞同步算法。 java
何爲cas? 算法
compare-and-swap (CAS) is an atomic instruction used in multithreading to achieve synchronization
簡單來講就是原子操做,系統支不支持CAS還要看cpu,如今幾乎全部的CPU指令都支持CAS的原子操做,X86下對應的是 CMPXCHG 彙編指令。cas指令須要三個操做數,非別是內存位置(V),舊值預期值(A)和新值。cas指令執行時,當且僅當V符合舊預期值A是,處理器用新的值B更新V的值,不然不執行更新。 編程
jdk1.5爲咱們提供java.util.concurrent併發包,提供很方便的原子操做類,底層實現由sun.misc.Unsaft類裏的compareAndWapInt和compareAndLong等幾個方法封裝調用,注意sun.misc.Unsaft是非標準的java api 。 api
非阻塞的計數器 多線程
使用AtomicInteger的compareAndSet方法,很是簡單實現非左塞的計數器,代碼比用同步塊實現多線程的計數器簡單的多。 併發
public class NonblockingCounter { private AtomicInteger value; public int getValue() { return value.get(); } public int increment() { int v; do { v = value.get(); while (!value.compareAndSet(v, v + 1)); return v + 1; } }
compareAndSet在一個死循環中,不斷嘗試將一個比當前值大1 賦值給本身,若是執行失敗,說明有其餘線程修改了值,只要從新循環執行一次操做,直到成功爲止。 性能
非阻塞的簡單鏈表 this
使用AtomicReference實現一個簡單的鏈表操做(add),整體類結構和普通的鏈表差很少。 atom
public class ConcurrentLinked<E> { private AtomicReference<Node<E>> first = new AtomicReference<Node<E>>(); public boolean add(E e) { Node<E> n = new Node<E>(e); while (first.get() == null && first.compareAndSet(null, n)) { return true; } for (;;) { Node<E> insertNode = findInsertionPlace(); if (insertNode.next.compareAndSet(null, n)) { break; } } return true; } public E getLast() { Node<E> a = getFirstNode(); if (a == null) return null; while (a.next.get() != null) { // 找插入位置 a = a.next.get(); } return a.item; } private Node<E> getFirstNode() { return this.first.get(); } private Node<E> findInsertionPlace() { Node<E> a = getFirstNode(); while (a.next.get() != null) { // 找插入位置 a = a.next.get(); } return a; } private static class Node<E> { E item; AtomicReference<Node<E>> next = new AtomicReference<Node<E>>(); public Node(E item) { this.item = item; } } }
cas看似很完美,可是不是全部的原子操做場景都適合,好比,多個變量同步原子更新。使用cas的地方,通常會把compareAndSet放到一個無限的循環中,在線程競爭比較激烈的狀況下,cpu消耗比較嚴重的。另外,cas自己有一個邏輯漏洞(俗稱"ABA"問題)。 spa
參考資料
《java併發編程實踐》