摘要:是獨占式鎖的一種,并且是不可重入的鎖,這篇文章是對鎖源代碼分析的預(yù)熱篇
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代碼實現(xiàn)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.初始狀態(tài) tail指向一個node(head)節(jié)點 * +------+ * | head | <---- tail * +------+ * * 2.lock-thread加入等待隊列: tail指向新的Node,同時Prev指向tail之前指向的節(jié)點 * +----------+ * | 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,線程僅在一個局部變量上自旋,它不斷輪詢前一個節(jié)點狀態(tài),如果發(fā)現(xiàn)前一個節(jié)點釋放鎖結(jié)束.
所以在java中使用了ThreadLocal作為具體實現(xiàn),AtomicReference為了消除多個線程并發(fā)對tail引用Node的影響,核心方法lock()中分為3個步驟去實現(xiàn)
初始狀態(tài) tail指向一個node(head)節(jié)點
private final AtomicReferencetail = new AtomicReference (new Node());
thread加入等待隊列: tail指向新的Node,同時Prev指向tail之前指向的節(jié)點,在java代碼中使用了getAndSet即CAS操作使用
Node pred = this.tail.getAndSet(node); this.prev.set(pred);
尋找當前線程對應(yīng)的node的前驅(qū)node然后開始自旋前驅(qū)node的status判斷是否可以獲取lock
while (pred.locked);
同理unlock()方法,獲取當前線程的node,設(shè)置lock status,將當前node指向前驅(qū)node(這樣操作tail指向的就是前驅(qū)node等同于出隊操作).至此CLH Lock的過程就結(jié)束了
測試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的值
結(jié)果發(fā)現(xiàn)輸出的值和預(yù)期一樣,CLH Lock完成了獨占式鎖的功能
CLH Lock是一種比較簡單的自旋鎖算法之一,因為鎖的CAS操作涉及到了硬件的鎖定(鎖總線或者是鎖內(nèi)存)所以性能和CPU架構(gòu)也密不可分,該興趣的同學(xué)可以繼續(xù)深入研究包括MCS鎖等。CLH Lock是獨占式鎖的一種,并且是不可重入的鎖,這篇文章是對AQS鎖源代碼分析的預(yù)熱篇
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/66114.html
摘要:在時,引入了包,該包中的大多數(shù)同步器都是基于來構(gòu)建的??蚣芴峁┝艘惶淄ㄓ玫臋C制來管理同步狀態(tài)阻塞喚醒線程管理等待隊列。指針用于在結(jié)點線程被取消時,讓當前結(jié)點的前驅(qū)直接指向當前結(jié)點的后驅(qū)完成出隊動作。 showImg(https://segmentfault.com/img/remote/1460000016012438); 本文首發(fā)于一世流云的專欄:https://segmentfau...
摘要:造成當前線程在接到信號被中斷或到達指定最后期限之前一直處于等待狀態(tài)。該線程從等待方法返回前必須獲得與相關(guān)的鎖。如果線程已經(jīng)獲取了鎖,則將喚醒條件隊列的首節(jié)點。 一、寫在前面 在前幾篇我們聊了 AQS、CLH、ReentrantLock、ReentrantReadWriteLock等的原理以及其源碼解讀,具體參見專欄 《非學(xué)無以廣才》 這章我們一起聊聊顯示的Condition 對象。 ...
摘要:接著線程過來通過方式獲取鎖,獲取鎖的過程就是通過操作變量將其值從變?yōu)?。線程加鎖成功后還有一步重要的操作,就是將設(shè)置成為自己。線程屁顛屁顛的就去等待區(qū)小憩一會去了。 一、寫在前面 這篇文章,我們聊一聊Java并發(fā)中的核武器, AQS底層實現(xiàn)。 不管是工作三四年、還是五六年的在工作或者面試中涉及到并發(fā)的是時候總是繞不過AQS這個詞。 首先,確實還有很多人連AQS是什么都不知道,甚至有的竟...
摘要:與之相關(guān)的方法有三個原子性地修改都是類型,可見我們可以進行,來定義的獲取與釋放從而實現(xiàn)我們自定義的同步器。 前言 源碼分析我認為主要有兩個作用:滿足好奇心,我想每一個有追求的人都不會滿足于僅僅做一個API Caller實現(xiàn)功能就好,我們也想知道它到底是怎么實現(xiàn)的;借鑒與升華,當我們明白了一個類的設(shè)計原理,在一定的情境下我們可以借鑒其設(shè)計哲學(xué),甚至針對我們自己特殊的業(yè)務(wù)場景對其進行改良與...
摘要:與之相關(guān)的方法有三個原子性地修改都是類型,可見我們可以進行,來定義的獲取與釋放從而實現(xiàn)我們自定義的同步器。 前言 源碼分析我認為主要有兩個作用:滿足好奇心,我想每一個有追求的人都不會滿足于僅僅做一個API Caller實現(xiàn)功能就好,我們也想知道它到底是怎么實現(xiàn)的;借鑒與升華,當我們明白了一個類的設(shè)計原理,在一定的情境下我們可以借鑒其設(shè)計哲學(xué),甚至針對我們自己特殊的業(yè)務(wù)場景對其進行改良與...
閱讀 2761·2021-11-16 11:45
閱讀 1670·2021-09-26 10:19
閱讀 2063·2021-09-13 10:28
閱讀 2822·2021-09-08 10:46
閱讀 1547·2021-09-07 10:13
閱讀 1545·2019-08-30 13:50
閱讀 1384·2019-08-30 11:17
閱讀 1464·2019-08-29 13:18