CLH队列锁
Posted yutingliuyl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CLH队列锁相关的知识,希望对你有一定的参考价值。
http://blog.csdn.net/aesop_wubo/article/details/7533186
CLH锁即Craig, Landin, and Hagersten (CLH) locks。CLH锁是一个自旋锁。能确保无饥饿性。提供先来先服务的公平性。
CLH锁也是一种基于链表的可扩展、高性能、公平的自旋锁,申请线程仅仅在本地变量上自旋,它不断轮询前驱的状态,假设发现前驱释放了锁就结束自旋。
SMP(Symmetric Multi-Processor)。即对称多处理器结构,指server中多个CPU对称工作,每一个CPU訪问内存地址所需时间同样。其主要特征是共享,包括对CPU,内存,I/O等进行共享。SMP的长处是可以保证内存一致性。缺点是这些共享的资源非常可能成为性能瓶颈。随着CPU数量的添加,每一个CPU都要訪问同样的内存资源,可能导致内存訪问冲突,可能会导致CPU资源的浪费。经常使用的PC机就属于这样的。
NUMA(Non-Uniform Memory Access)非一致存储訪问,将CPU分为CPU模块,每一个CPU模块由多个CPU组成,而且具有独立的本地内存、I/O槽口等,模块之间能够通过互联模块相互訪问,訪问本地内存的速度将远远高于訪问远地内存(系统内其他节点的内存)的速度,这也是非一致存储訪问NUMA的由来。NUMA长处是能够较好地解决原来SMP系统的扩展问题,缺点是因为訪问远地内存的延时远远超过本地内存,因此当CPU数量添加时。系统性能无法线性添加。
CLH算法实现
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()操作来实现的,它可以保证以原子方式更新对象引用。
- public class CLHLock implements Lock {
- AtomicReference<QNode> tail = new AtomicReference<QNode>(new QNode());
- ThreadLocal<QNode> myPred;
- ThreadLocal<QNode> myNode;
- public CLHLock() {
- tail = new AtomicReference<QNode>(new QNode());
- myNode = new ThreadLocal<QNode>() {
- protected QNode initialValue() {
- return new QNode();
- }
- };
- myPred = new ThreadLocal<QNode>() {
- protected QNode initialValue() {
- return null;
- }
- };
- }
- @Override
- public void lock() {
- QNode qnode = myNode.get();
- qnode.locked = true;
- QNode pred = tail.getAndSet(qnode);
- myPred.set(pred);
- while (pred.locked) {
- }
- }
- @Override
- public void unlock() {
- QNode qnode = myNode.get();
- qnode.locked = false;
- myNode.set(myPred.get());
- }
- }
CLH优缺点
CLH队列锁的长处是空间复杂度低(假设有n个线程。L个锁,每一个线程每次仅仅获取一个锁,那么须要的存储空间是O(L+n),n个线程有n个myNode。L个锁有L个tail),CLH的一种变体被应用在了JAVA并发框架中。唯一的缺点是在NUMA系统结构下性能非常差。在这样的系统结构下,每一个线程有自己的内存,假设前趋结点的内存位置比較远。自旋推断前趋结点的locked域,性能将大打折扣,可是在SMP系统结构下该法还是非常有效的。
一种解决NUMA系统结构的思路是MCS队列锁。
參考资料:
The Art of Multiprocessor Programming
以上是关于CLH队列锁的主要内容,如果未能解决你的问题,请参考以下文章
[Java并发] AQS抽象队列同步器源码解析--锁获取过程