上一篇分析了CLH鎖,這篇分析一下MCS鎖。若是沒有看過上一篇的話,建議先看一下 CLH鎖簡介,本篇是在上一篇的基礎上作了簡單的描述。java
MCS 的名字也是由發明人的名字字幕組合來的(John Mellor-Crummey and Michael Scott)。MCS與CLH最大的不一樣是,CLH在前驅節點上判斷是否得到鎖,MCS則是在自身的節點上判斷是否可以獲取到鎖。node
public class MCSLock { public class QNode { volatile boolean locked = false; QNode next = null; } private AtomicReference<QNode> tail; private ThreadLocal<QNode> myNode; public MCSLock() { tail = new AtomicReference<QNode>(null); myNode = new ThreadLocal<QNode>() { protected QNode initialValue() { return new QNode(); } }; } public void lock() { QNode qnode = myNode.get(); QNode pred = tail.getAndSet(qnode); if (null != pred) { qnode.locked = true; pred.next = qnode; } while (qnode.locked) { } } public void unlock() { QNode qnode = myNode.get(); if (null == qnode.next) { if (tail.compareAndSet(qnode, null)) { return; } while (null == qnode.next) { } } qnode.next.locked = false; qnode.next = null; } }
MCS一樣有一個tail節點,也是存放最後一次申請鎖的對象,另外它只有一個myNode屬性。它的QNode對象內部除了維護一個boolean類型的locked變量,還有一個QNode類型的next變量,用來指向下一個節點。CLH鎖等待隊列是一個邏輯上的隊列,而MCS鎖的等待隊列是一個顯示的單向鏈表。spa
lock方法先將自身的locked屬性設置爲true,將tail節點上的對象的next變量設置爲本身,而後在自身的locked變量上等待鎖。.net
unlock方法先判斷後面有沒有對象在等待獲取鎖,若是有的話將後面對象的locked變量設置爲false,即通知後面的對象能夠執行任務了,隨即將自身的next至空(由於是ThreadLocal類型的變量,用完清理,在上一篇講解CLH鎖的時候有說明),若是後面沒有對象在等待的話,將tail節點至空(這裏是一個CAS操做),若是CAS至空操做失敗,證實後面又有對象申明瞭鎖,下面的 while(null == qnode.next) 循環則是爲了確保後面的對象被成功加入到等待隊列(鏈表)的保護工做。code