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()操作来实现的,它可以保证以原子方式更新对象引用。
[java] view
plain
copy

  1. public class CLHLock implements Lock {
  2. AtomicReference<QNode> tail = new AtomicReference<QNode>(new QNode());
  3. ThreadLocal<QNode> myPred;
  4. ThreadLocal<QNode> myNode;
  5. public CLHLock() {
  6. tail = new AtomicReference<QNode>(new QNode());
  7. myNode = new ThreadLocal<QNode>() {
  8. protected QNode initialValue() {
  9. return new QNode();
  10. }
  11. };
  12. myPred = new ThreadLocal<QNode>() {
  13. protected QNode initialValue() {
  14. return null;
  15. }
  16. };
  17. }
  18. @Override
  19. public void lock() {
  20. QNode qnode = myNode.get();
  21. qnode.locked = true;
  22. QNode pred = tail.getAndSet(qnode);
  23. myPred.set(pred);
  24. while (pred.locked) {
  25. }
  26. }
  27. @Override
  28. public void unlock() {
  29. QNode qnode = myNode.get();
  30. qnode.locked = false;
  31. myNode.set(myPred.get());
  32. }
  33. }

从代码中能够看出lock方法中有一个while循环,这 是在等待前趋结点的locked域变为false。这是一个自旋等待的过程。unlock方法非常easy。仅仅须要将自己的locked域设置为false就可以。


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队列锁的更多相关文章

  1. 【Java并发编程实战】-----“J.U.C”:CLH队列锁

    在前面介绍的几篇博客中总是提到CLH队列,在AQS中CLH队列是维护一组线程的严格按照FIFO的队列.他能够确保无饥饿,严格的先来先服务的公平性.下图是CLH队列节点的示意图: 在CLH队列的节点QN ...

  2. 可重入锁 公平锁 读写锁、CLH队列、CLH队列锁、自旋锁、排队自旋锁、MCS锁、CLH锁

    1.可重入锁 如果锁具备可重入性,则称作为可重入锁. ========================================== (转)可重入和不可重入 2011-10-04 21:38 这 ...

  3. Java 并发编程学习笔记 理解CLH队列锁算法

    CLH算法实现 CLH队列中的结点QNode中含有一个locked字段,该字段若为true表示该线程需要获取锁,且不释放锁,为false表示线程释放了锁.结点之间是通过隐形的链表相连,之所以叫隐形的链 ...

  4. 【Java并发编程实战】-----“J.U.C”:CLH队列锁

    在前面介绍的几篇博客中总是提到CLH队列,在AQS中CLH队列是维护一组线程的严格依照FIFO的队列.他可以确保无饥饿,严格的先来先服务的公平性.下图是CLH队列节点的示意图: 在CLH队列的节点QN ...

  5. 并发之AQS原理(二) CLH队列与Node解析

    并发之AQS原理(二) CLH队列与Node解析 1.CLH队列与Node节点 就像通常医院看病排队一样,医生一次能看的病人数量有限,那么超出医生看病速度之外的病人就要排队. 一条队列是队列中每一个人 ...

  6. 并发系列(3)之 CLH、MCS 队列锁简介

    这篇博客主要是作为 AbstractQueuedSynchronizer 的背景知识介绍:平时接触也非常的少,如果你不感兴趣可以跳过:但是了解一下能更加的清楚 AQS 的设计思路: 一.自旋锁简介 通 ...

  7. 从ReentrantLock实现非公平锁的源码理解AQS中的CLH队列

    虽然前面也看过AQS的文章,并且转载过一篇大佬的分析,但是我觉得他们对于AQS和ReentrantLock部分的源码的分析并不详细,自己理解期来还是有问题,于是自己准备花时间重新梳理下,好了,进入正题 ...

  8. AQS(一) 对CLH队列的增强

    基本概念 AQS(AbstractQueuedSynchronizer),顾名思义,是一个抽象的队列同步器. 它的队列是先进先出(FIFO)的等待队列 基于这个队列,AQS提供了一个实现阻塞锁的机制 ...

  9. 【Java并发编程实战】----- AQS(四):CLH同步队列

    在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形.其主要从两方面进行了改造:节点的结构与节点等待机制.在结构上引入了头 ...

随机推荐

  1. shell more less cat

    cat 连续显示.查看文件内容 more 分页查看文件内容 less 分页可控制查看文件内容 通俗点说: cat一次性把文件内容全部显示出来,管你看不看得清,显示完了cat命令就返回了,不能进行交互式 ...

  2. 如何在Linux中用命令行工具管理KVM虚拟环境

    第一步: 配置存储池 Virsh命令行工具是一款管理virsh客户域的用户界面,它能在命令行中运行所给的命令以及它的参数,我们要用它给我们的KVM环境创建存储池,想知道关于这个工具的更多信息,用以下这 ...

  3. WordPress 后台上传自定义网站Logo

    需求: 众所周知一般网站的logo都是固定的所以我在做网站时也是使用的静态logo文件,但最近用wp给一个客户做的网站时,因为网站现在的logo可能会需要重新设计,所以客户提出了需要在后台可以自己修改 ...

  4. Mapnik读取PostGIS数据渲染图片

    __author__ = 'Administrator' # encoding: utf-8 import sys import datetime import mapnik m = mapnik.M ...

  5. MYSQL 表中汉字写入或字段赋值时乱码情况排误

    -- 当改动字段值.或是直接写入时,汉字变成乱码情况 .[可注意一下数据库名,记得改动] -- 当字符顺序对汉字不兼容时,可能直接导致乱码情况发生. 最好做到库.表.字段(字符类型)排序规则是否一致 ...

  6. C#.NET常见问题(FAQ)-Combobox如何设置不可以编辑

    设置它的DropDownStyle为DropDownList即可     更多教学视频和资料下载,欢迎关注以下信息: 我的优酷空间: http://i.youku.com/acetaohai123   ...

  7. js随机码之乱序数组

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. WebStorm配置

    一.主题配色 主题设置方法:File -> Settings -> Appearance & Behavior -> Appearance ->Theme. webst ...

  9. Ubuntu12.04+OpenERP7.0安装笔记

    不经意的一次看到OpenERP这个开源ERP,就被其丰富的功能,简洁的画面,熟悉的语言所吸引.迫不及待的多方查询资料,自己架设一个测试环境来进行了解.以下为测试安装时候的步骤说明,以备查询,并供有需要 ...

  10. mysql的常用函数、流程控制

    case when用法: END END if用法 IF(expr1,expr2,expr3) #表达式expr1为真则返回expr2否则为expr3 , , ) #结果: 1 ifnull用法: I ...