CLH lock is Craig, Landin, and Hagersten (CLH) locks, CLH lock is a spin lock, can ensure no hunger, provide fairness first come first service.
The CLH lock is a scalable, high performance, fairness and spin lock based on the list, the application thread spin only on a local variable, it constantly polling the precursor state, if it is found that the pre release lock end spin.java
CLH鎖是自旋鎖的一種,對它的研究是由於AQS源代碼中使用了CLH鎖的一個變種,爲了更好的理解AQS中使用鎖的思想,因此決定先好好理解CLH鎖node
public class ClhSpinLock implements Lock{ private final ThreadLocal<Node> prev; private final ThreadLocal<Node> node; private final AtomicReference<Node> tail = new AtomicReference<Node>(new Node()); public ClhSpinLock() { this.node = new ThreadLocal<Node>() { protected Node initialValue() { return new Node(); } }; this.prev = new ThreadLocal<Node>() { protected Node initialValue() { return null; } }; } /** * 1.初始狀態 tail指向一個node(head)節點 * +------+ * | head | <---- tail * +------+ * * 2.lock-thread加入等待隊列: tail指向新的Node,同時Prev指向tail以前指向的節點 * +----------+ * | Thread-A | * | := Node | <---- tail * | := Prev | -----> +------+ * +----------+ | head | * +------+ * * +----------+ +----------+ * | Thread-B | | Thread-A | * tail ----> | := Node | --> | := Node | * | := Prev | ----| | := Prev | -----> +------+ * +----------+ +----------+ | head | * +------+ * 3.尋找當前node的prev-node而後開始自旋 * */ public void lock() { final Node node = this.node.get(); node.locked = true; Node pred = this.tail.getAndSet(node); this.prev.set(pred); // 自旋 while (pred.locked); } public void unlock() { final Node node = this.node.get(); node.locked = false; this.node.set(this.prev.get()); } @Override public void lockInterruptibly() throws InterruptedException { // TODO Auto-generated method stub } @Override public boolean tryLock() { // TODO Auto-generated method stub return false; } @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { // TODO Auto-generated method stub return false; } @Override public Condition newCondition() { // TODO Auto-generated method stub return null; } private static class Node { private volatile boolean locked; } }
簡單的看一下CLH的算法定義算法
the list, the application thread spin only on a local variable, it constantly polling the precursor state, if it is found that the pre release lock end spin.架構
基於list,線程僅在一個局部變量上自旋,它不斷輪詢前一個節點狀態,若是發現前一個節點釋放鎖結束.併發
因此在java中使用了ThreadLocal做爲具體實現,AtomicReference爲了消除多個線程併發對tail引用Node的影響,核心方法lock()中分爲3個步驟去實現app
初始狀態 tail指向一個node(head)節點ide
private final AtomicReference<Node> tail = new AtomicReference<Node>(new Node());
thread加入等待隊列: tail指向新的Node,同時Prev指向tail以前指向的節點,在java代碼中使用了getAndSet即CAS操做使用性能
Node pred = this.tail.getAndSet(node); this.prev.set(pred);
尋找當前線程對應的node的前驅node而後開始自旋前驅node的status判斷是否能夠獲取lock測試
while (pred.locked);
同理unlock()方法,獲取當前線程的node,設置lock status,將當前node指向前驅node(這樣操做tail指向的就是前驅node等同於出隊操做).至此CLH Lock的過程就結束了this
public class LockTest { static int count = 0; public static void testLock(Lock lock) { try { lock.lock(); for (int i = 0; i < 10000000; i++) ++count; } finally { lock.unlock(); } } public static void main(String[] args) throws InterruptedException, BrokenBarrierException { final ClhSpinLock clh = new ClhSpinLock(); final CyclicBarrier cb = new CyclicBarrier(10, new Runnable() { @Override public void run() { System.out.println(count); } }); for (int i = 0; i < 10; i++) { new Thread(new Runnable() { @Override public void run() { testLock(clh); // 這段代碼是非lock比較使用 // for (int i = 0; i < 10000000; i++) // count++; try { cb.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } } }).start(); } } }
測試代碼使用了CyclicBarrier輔助當全部子線程完成後輸出static變量count的值
結果發現輸出的值和預期同樣,CLH Lock完成了獨佔式鎖的功能
CLH Lock是一種比較簡單的自旋鎖算法之一,由於鎖的CAS操做涉及到了硬件的鎖定(鎖總線或者是鎖內存)因此性能和CPU架構也密不可分,該興趣的同窗能夠繼續深刻研究包括MCS鎖等。CLH Lock是獨佔式鎖的一種,而且是不可重入的鎖,這篇文章是對AQS鎖源代碼分析的預熱篇