1. 程式人生 > >Java併發框架——AQS阻塞佇列管理(二)——自旋鎖優化

Java併發框架——AQS阻塞佇列管理(二)——自旋鎖優化

看Craig, Landin, and Hagersten發明的CLH鎖如何優化同步帶來的花銷,其核心思想是:通過一定手段將所有執行緒對某一共享變數輪詢競爭轉化為一個執行緒佇列且佇列中的執行緒各自輪詢自己的本地變數。這個轉化過程由兩個要點,一是構建怎樣的佇列&如何構建佇列,為了保證公平性,構建的將是一個FIFO佇列,構建的時候主要通過移動尾部節點tail實現佇列的排隊,每個想獲取鎖的執行緒建立一個新節點並通過CAS原子操作將新節點賦予tail,然後讓當前執行緒輪詢前一節點的某個狀態位,如圖2-5-9-3,如此就成功構建執行緒排隊佇列;二是如何釋放佇列,執行完執行緒後只需將當前執行緒對應的節點狀態位置為解鎖狀態,由於下一節點一直在輪詢,可獲取到鎖。
 
圖2-5-9-3 CLH鎖
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);
}


}

點選訂購作者書籍《Tomcat核心設計剖析》