上一篇分析了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