摘要:是獨占式鎖的一種,并且是不可重入的鎖,這篇文章是對鎖源代碼分析的預熱篇
CLH Lock摘要
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.
CLH鎖是自旋鎖的一種,對它的研究是因為AQS源代碼中使用了CLH鎖的一個變種,為了更好的理解AQS中使用鎖的思想,所以決定先好好理解CLH鎖
Java代碼實現public class ClhSpinLock implements Lock{ private final ThreadLocalprev; private final ThreadLocal node; private final AtomicReference tail = new AtomicReference (new Node()); public ClhSpinLock() { this.node = new ThreadLocal () { protected Node initialValue() { return new Node(); } }; this.prev = new ThreadLocal () { 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個步驟去實現
初始狀態 tail指向一個node(head)節點
private final AtomicReferencetail = new AtomicReference (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的過程就結束了
測試ClhSpinLockpublic 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鎖源代碼分析的預熱篇
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/66114.html
摘要:在時,引入了包,該包中的大多數同步器都是基于來構建的。框架提供了一套通用的機制來管理同步狀態阻塞喚醒線程管理等待隊列。指針用于在結點線程被取消時,讓當前結點的前驅直接指向當前結點的后驅完成出隊動作。 showImg(https://segmentfault.com/img/remote/1460000016012438); 本文首發于一世流云的專欄:https://segmentfau...
摘要:造成當前線程在接到信號被中斷或到達指定最后期限之前一直處于等待狀態。該線程從等待方法返回前必須獲得與相關的鎖。如果線程已經獲取了鎖,則將喚醒條件隊列的首節點。 一、寫在前面 在前幾篇我們聊了 AQS、CLH、ReentrantLock、ReentrantReadWriteLock等的原理以及其源碼解讀,具體參見專欄 《非學無以廣才》 這章我們一起聊聊顯示的Condition 對象。 ...
摘要:接著線程過來通過方式獲取鎖,獲取鎖的過程就是通過操作變量將其值從變為。線程加鎖成功后還有一步重要的操作,就是將設置成為自己。線程屁顛屁顛的就去等待區小憩一會去了。 一、寫在前面 這篇文章,我們聊一聊Java并發中的核武器, AQS底層實現。 不管是工作三四年、還是五六年的在工作或者面試中涉及到并發的是時候總是繞不過AQS這個詞。 首先,確實還有很多人連AQS是什么都不知道,甚至有的竟...
摘要:與之相關的方法有三個原子性地修改都是類型,可見我們可以進行,來定義的獲取與釋放從而實現我們自定義的同步器。 前言 源碼分析我認為主要有兩個作用:滿足好奇心,我想每一個有追求的人都不會滿足于僅僅做一個API Caller實現功能就好,我們也想知道它到底是怎么實現的;借鑒與升華,當我們明白了一個類的設計原理,在一定的情境下我們可以借鑒其設計哲學,甚至針對我們自己特殊的業務場景對其進行改良與...
摘要:與之相關的方法有三個原子性地修改都是類型,可見我們可以進行,來定義的獲取與釋放從而實現我們自定義的同步器。 前言 源碼分析我認為主要有兩個作用:滿足好奇心,我想每一個有追求的人都不會滿足于僅僅做一個API Caller實現功能就好,我們也想知道它到底是怎么實現的;借鑒與升華,當我們明白了一個類的設計原理,在一定的情境下我們可以借鑒其設計哲學,甚至針對我們自己特殊的業務場景對其進行改良與...
閱讀 2756·2021-11-16 11:45
閱讀 1663·2021-09-26 10:19
閱讀 2058·2021-09-13 10:28
閱讀 2815·2021-09-08 10:46
閱讀 1544·2021-09-07 10:13
閱讀 1539·2019-08-30 13:50
閱讀 1382·2019-08-30 11:17
閱讀 1462·2019-08-29 13:18