`

【转】JAVA并发编程学习笔记之CLH队列锁

 
阅读更多

【转】http://blog.csdn.net/aesop_wubo/article/details/7533186

NUMA与SMP

SMP(Symmetric Multi-Processor),即对称多处理器结构,指服务器中多个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()操作来实现的,它能够保证以原子方式更新对象引用。
  1. public class CLHLock implements Lock {  
  2.     AtomicReference<QNode> tail = new AtomicReference<QNode>(new QNode());  
  3.     ThreadLocal<QNode> myPred;  
  4.     ThreadLocal<QNode> myNode;  
  5.   
  6.     public CLHLock() {  
  7.         tail = new AtomicReference<QNode>(new QNode());  
  8.         myNode = new ThreadLocal<QNode>() {  
  9.             protected QNode initialValue() {  
  10.                 return new QNode();  
  11.             }  
  12.         };  
  13.         myPred = new ThreadLocal<QNode>() {  
  14.             protected QNode initialValue() {  
  15.                 return null;  
  16.             }  
  17.         };  
  18.     }  
  19.   
  20.     @Override  
  21.     public void lock() {  
  22.         QNode qnode = myNode.get();  
  23.         qnode.locked = true;  
  24.         QNode pred = tail.getAndSet(qnode);  
  25.         myPred.set(pred);  
  26.         while (pred.locked) {  
  27.         }  
  28.     }  
  29.   
  30.     @Override  
  31.     public void unlock() {  
  32.         QNode qnode = myNode.get();  
  33.         qnode.locked = false;  
  34.         myNode.set(myPred.get());  
  35.     }  
  36. }     
从代码中可以看出lock方法中有一个while循环,这 是在等待前趋结点的locked域变为false,这是一个自旋等待的过程。unlock方法很简单,只需要将自己的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
分享到:
评论

相关推荐

    JAVA并发编程与高并发解决方案-并发编程四之J.U.C之AQS.docx

    ### JAVA并发编程与高并发解决方案-并发编程四之J.U.C之AQS #### 引言 《JAVA并发编程与高并发解决方案-并发编程四之J.U.C之AQS》是一篇详细介绍Java实用并发工具包(Java Util Concurrency,简称J.U.C.)中重要...

    C语言也能做大事1笔记(CLH)

    C语言也能做大事1笔记(CLH)C语言也能做大事1笔记(CLH)C语言也能做大事1笔记(CLH)C语言也能做大事1笔记(CLH)C语言也能做大事1笔记(CLH)C语言也能做大事1笔记(CLH)C语言也能做大事1笔记(CLH)C语言也能做大事1笔记(CLH)...

    joeylv#joscrapy#【Java并发编程实战】-----AQS(四):CLH同步队列1

    在线程获取锁时会调用AQS的acquire()方法,该方法第一次尝试获取锁如果失败,会将该线程加入到CLH队列中:public final void acqui

    java并发编程:juc、aqs

    Java并发编程中的`JUC`(Java Util Concurrency)库是Java平台中用于处理多线程问题的核心工具包,它提供了一系列高效、线程安全的工具类,帮助开发者编写并发应用程序。`AQS`(AbstractQueuedSynchronizer)是JUC库中的...

    浅谈Java并发 J.U.C之AQS:CLH同步队列

    浅谈Java并发 J.U.C之AQS:CLH同步队列 在 Java 并发编程中,J.U.C(Java Utility Classes)提供了一些高效的并发工具,其中AQS(AbstractQueuedSynchronizer)是一个核心组件之一。AQS内部维护着一个FIFO队列,即...

    新手乐园笔记(CLH) 新手乐园笔记(CLH)

    【新手乐园笔记(CLH)】是一份专为IT初学者设计的学习资料,旨在帮助新手快速熟悉并掌握计算机基础知识和常见技术。这份笔记涵盖了多个重要领域,包括但不限于操作系统、网络基础、编程语言入门、数据结构与算法、...

    C语言高级编程技术(CLH)

    C语言高级编程技术(CLH)C语言高级编程技术(CLH)C语言高级编程技术(CLH)C语言高级编程技术(CLH)C语言高级编程技术(CLH)C语言高级编程技术(CLH)C语言高级编程技术(CLH)C语言高级编程技术(CLH)C语言高级编程技术(CLH)...

    windows编程模型(CLH)

    windows编程模型(CLH)windows编程模型(CLH)windows编程模型(CLH)windows编程模型(CLH)windows编程模型(CLH)windows编程模型(CLH)windows编程模型(CLH)windows编程模型(CLH)windows编程模型(CLH)windows编程...

    c与c++嵌入式系统编程(CLH)

    c与c++嵌入式系统编程(CLH)c与c++嵌入式系统编程(CLH)c与c++嵌入式系统编程(CLH)c与c++嵌入式系统编程(CLH)c与c++嵌入式系统编程(CLH)c与c++嵌入式系统编程(CLH)c与c++嵌入式系统编程(CLH)c与c++嵌入式系统编程(CLH)...

    并发数据结构与多核编程21-22秋季1

    理解这些概念对于深入学习并发编程至关重要。接着,课程详细讲解了共享存储器的性质,这对于理解多核处理器中内存的并发访问模式非常有帮助。同步原语是并发程序设计中的核心,课程讲解了多种互斥算法,如彼得森算法...

    【并发编程】简单化理解AQS和ReentrantLock.pdf

    AQS和`ReentrantLock`是Java并发编程中重要的组成部分,通过对它们的理解和掌握,可以更好地设计和实现高性能的并发程序。通过本文的学习,读者可以了解到这些核心概念和技术的实际应用,并能够根据具体的业务需求...

    windows 编程基础(CLH)

    windows 编程基础(CLH)windows 编程基础(CLH)windows 编程基础(CLH)windows 编程基础(CLH)windows 编程基础(CLH)windows 编程基础(CLH)windows 编程基础(CLH)windows 编程基础(CLH)windows 编程基础(CLH)

    Java面试题并发部分.docx

    在Java并发编程中,面试时常会涉及到一系列的关键概念和技术,这些是确保多线程环境下的程序正确性和性能的重要因素。以下是对这些知识点的详细解释: 1. **死锁**:死锁是指两个或多个线程在执行过程中,因争夺...

    aqs_java_

    4. **CLH锁队列**:AQS的等待队列采用CLH(Craig, Landin, and Hagersten)锁队列结构,这是一种高效的自旋锁实现,每个节点代表一个线程,前驱节点即为阻塞它的线程。 5. **节点状态**:节点状态包括PARKED(已...

    并发锁核心类AQS学习笔记

    一、概念 AQS 是 AbstractQueuedSynchronizer 的简称,AQS 是一个抽象的队列式同步器框架,提供了...等到占有线程释放锁后唤醒队列中的任务争抢锁,这个队列为 CLH 队列。 使用state成员变量表示当前的同步状态,提供

    Java concurrency之公平锁(一)_动力节点Java学院整理

    【Java并发编程】公平锁详解 在Java并发编程中,锁是控制多线程访问共享资源的重要工具。本文主要探讨的是Java并发库(Java Concurrency in Practice, JUC)中的公平锁,它遵循先来先服务(First-Come First-Served...

    尚硅谷大厂面试题第三季周阳主讲

    【标题】"尚硅谷大厂面试题第三季周阳主讲"主要涵盖了Java后端面试中的核心知识点,包括Java并发编程(JUC)、Redis缓存系统以及Spring框架的应用。本内容整理了B站尚硅谷频道关于大厂面试的全部资料,帮助考生全面...

    AQS源码阅读笔记,画了两三天的AQS...

    AQS(AbstractQueuedSynchronizer)是Java并发编程中的一种同步器框架,它提供了一个队列来管理线程的排队和唤醒机制。下面是AQS源码阅读笔记的详细解释: 1. `ReentrantLock` 的 `unlock()` 方法:释放锁的基本...

    深入理解Java中的AQS.docx

    总之,AQS是Java并发库的核心,通过其提供的模板方法和内部队列机制,实现了高效、灵活的线程同步和锁管理,为开发者提供了强大的并发编程工具。理解和掌握AQS的原理对于编写高性能、线程安全的Java应用至关重要。

    7 AQS源码分析.docx

    AQS,即AbstractQueuedSynchronizer,是Java并发编程中的重要组件,主要用于构建锁和同步器。它基于一种称为CLH(Craig, Landin, and Hagersten)队列的等待队列实现,是Java并发包`java.util.concurrent.locks`中的...

Global site tag (gtag.js) - Google Analytics