JDK 併發 AQS 系列(四)
自旋鎖的不足
前面說到用自旋方式來獲取鎖,能有效避免執行緒掛起和恢復。但它也有不足之處:
-
僅適用於佔用時間短、顆粒度很小的情景。
-
需要硬體級別的原子操作。
-
它無法保證公平性。
-
每次讀寫操作需要同步每個處理器的快取。
CLH鎖
鑑於自旋鎖的不足,Craig,Landin, Hagersten發明了CLH鎖,用來優化同步帶來的花銷。
核心思想
CLH鎖通過一定的手段將所有執行緒對某一共享變數輪詢競爭轉化為一個執行緒佇列且佇列中的執行緒各自輪詢自己的本地變數。
這個轉化過程有兩個要點:
-
構建怎樣的佇列&如何構建佇列?為了保證公平性,將構建一個FIFO佇列,構建的時候主要通過移動尾部節點tail實現佇列的排隊,每個想獲取鎖的執行緒建立一個新節點並通過CAS原子操作將新節點賦予tail,然後讓當前執行緒輪詢前一節點的某個狀態位,如此就成功構建執行緒排隊佇列;
-
如何釋放佇列,執行完執行緒後只需將當前執行緒對應的節點狀態位置為解鎖狀態,由於下一節點一直在輪詢,可獲取到鎖。

CLH鎖的核心思想貌似是將眾多執行緒長時間對某資源的競爭,通過有序化這些執行緒轉化為只需對本地變數檢測。唯一存在競爭的地方就是在入佇列之前對尾節點tail的競爭,但競爭的執行緒的數量已經少了很多,且比起所有執行緒直接對某資源競爭的輪詢次數也減少了很多,節省了很多CPU快取同步操作,大大提升系統性能,利用空間換取效能。
虛擬碼實現
下面提供一個簡單的CLH鎖實現程式碼,lock與unlock兩方法提供加鎖解鎖操作,每次加鎖解鎖必須將一個CLHNode物件作為引數傳入,lock方法的for迴圈是通過CAS操作將新節點插入佇列,而while迴圈則是檢測前驅節點的鎖狀態位,一旦前驅節點鎖狀態位允許則結束檢測讓執行緒往下執行。
解鎖操作先判斷當前節點是否為尾節點,如是則直接將尾節點置為空,此時說明僅僅只有一條執行緒在執行,否則將當前節點的鎖狀態位置為解鎖狀態。
public class CLHLock { private static Unsafe unsafe = null; private static final long valueOffset; private volatile CLHNode tail; public class CLHNode { private boolean isLocked = true; } static { try { unsafe = getUnsafeInstance(); valueOffset = unsafe.objectFieldOffset(CLHLock.class.getDeclaredField("tail")); } catch (Exception ex) { throw new Error(ex); } } public void lock(CLHNode currentThreadNode) { CLHNode preNode = null; for (;;) { preNode = tail; if (unsafe.compareAndSwapObject(this, valueOffset, tail, currentThreadNode)) break; } if (preNode != null) while (preNode.isLocked) { } } public void unlock(CLHNode currentThreadNode) { if (!unsafe.compareAndSwapObject(this, valueOffset, currentThreadNode, null)) currentThreadNode.isLocked = false; } private static Unsafe getUnsafeInstance() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafeInstance.setAccessible(true); return (Unsafe) theUnsafeInstance.get(Unsafe.class); } }
--------------------------------------
跟我交流:
-------------推薦閱讀------------
ofollow,noindex">我的開源專案彙總(機器&深度學習、NLP、網路IO、AIML、mysql協議、chatbot)
2017文章彙總——Java及中介軟體
2017文章彙總——JDK原始碼篇