Java 併發編程學習筆記 理解CLH隊列鎖算法

CLH算法實現html

CLH隊列中的結點QNode中含有一個locked字段,該字段若爲true表示該線程須要獲取鎖,且不釋放鎖,爲false表示線程釋放了鎖。結點之間是經過隱形的鏈表相連,之因此叫隱形的鏈表是由於這些結點之間沒有明顯的next指針,而是經過myPred所指向的結點的變化狀況來影響myNode的行爲。CLHLock上還有一個尾指針,始終指向隊列的最後一個結點。CLHLock的類圖以下所示:
 
 
 
當一個線程須要獲取鎖時,會建立一個新的QNode,將其中的locked設置爲true表示須要獲取鎖,而後線程對tail域調用getAndSet方法,使本身成爲隊列的尾部,同時獲取一個指向其前趨的引用myPred,而後該線程就在前趨結點的locked字段上旋轉,直到前趨結點釋放鎖。當一個線程須要釋放鎖時,將當前結點的locked域設置爲false,同時回收前趨結點。以下圖所示,線程A須要獲取鎖,其myNode域爲true,些時tail指向線程A的結點,而後線程B也加入到線程A後面,tail指向線程B的結點。而後線程A和B都在它的myPred域上旋轉,一量它的myPred結點的locked字段變爲false,它就能夠獲取鎖掃行。明顯線程A的myPred locked域爲false,此時線程A獲取到了鎖。
 
 
 
 
 
整個CLH的代碼以下,其中用到了ThreadLocal類,將QNode綁定到每個線程上,同時用到了AtomicReference,對尾指針的修改正是調用它的getAndSet()操做來實現的,它可以保證以原子方式更新對象引用。
 
 1 public class CLHLock {  2     
 3     AtomicReference<QNode> tail = new AtomicReference<QNode>(new QNode());  4     ThreadLocal<QNode> myPred;  5     ThreadLocal<QNode> myNode;  6   
 7     public static class QNode {  8         //注意這個地方 若是不加volatile則會致使線程永遠死循環  9         //關於volatile的用法在個人另一篇文章 http://www.cnblogs.com/daxin/p/3364014.html
10         public volatile boolean locked = false; 11  } 12     
13     public CLHLock() { 14         myNode = new ThreadLocal<QNode>() { 15             protected QNode initialValue() { 16                 return new QNode(); 17  } 18  }; 19         myPred = new ThreadLocal<QNode>() { 20             protected QNode initialValue() { 21                 return null; 22  } 23  }; 24  } 25   
26     public void lock() { 27         QNode qnode = myNode.get(); 28         qnode.locked = true; 29         QNode pred = tail.getAndSet(qnode); 30  myPred.set(pred); 31         while (pred.locked) { 32             //非阻塞算法
33  } 34  } 35   
36     public void unlock() { 37         QNode qnode = myNode.get(); 38         qnode.locked = false; 39  myNode.set(myPred.get()); 40  } 41 } 

 

 
從代碼中能夠看出lock方法中有一個while循環,這 是在等待前趨結點的locked域變爲false,這是一個自旋等待的過程。unlock方法很簡單,只須要將本身的locked域設置爲false便可。

CLH優缺點node

CLH隊列鎖的優勢是空間複雜度低(若是有n個線程,L個鎖,每一個線程每次只獲取一個鎖,那麼須要的存儲空間是O(L+n),n個線程有n個myNode,L個鎖有L個tail),CLH的一種變體被應用在了JAVA併發框架中。惟一的缺點是在NUMA系統結構下性能不好,在這種系統結構下,每一個線程有本身的內存,若是前趨結點的內存位置比較遠,自旋判斷前趨結點的locked域,性能將大打折扣,可是在SMP系統結構下該法仍是很是有效的。一種解決NUMA系統結構的思路是MCS隊列鎖。算法

相關文章
相關標籤/搜索