`
ChristmasLin
  • 浏览: 42090 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

线程锁系列(1):CLH Lock

阅读更多

 

最近在看关于线程锁的算法,做一个整理。资料出自于《The Art of Multiprocessor Programming》,算是一个读书笔记吧。示范代码基于java。

第一章是Craig, Landin, and Hagersten (CLH) locks。先上CLHLock的实现逻辑:

public class CLHLock implements Lock {
    private final AtomicReference tail;
    private final ThreadLocal myPred;
    private final ThreadLocal myNode;

    public CLHLock() {
        tail = new AtomicReference(new QNode());
        myNode = new ThreadLocal() {
            protected QNode initialValue() {
                return new QNode();
            }
        };

        myPred = new ThreadLocal();
    }

    @Override
    public void lock() {
        QNode node = myNode.get();
        node.locked = true;
        QNode pred = tail.getAndSet(node);
        myPred.set(pred);
        while (pred.locked) {
        }
    }

    @Override
    public void unlock() {
        QNode node = myNode.get();
        node.locked = false;
        myNode.set(myPred.get());
    }

    private static class QNode {
        volatile boolean locked;
    }
}



CLH Lock是一个自旋锁,能确保无饥饿性,提供先来先服务的公平性。

 

锁本身被表示成QNode的虚拟链表。共享的tail保存申请的最后的一个申请lock的线程的myNode域。每个申请lock的线程通过一个本地线程变量pred指向它的前驱。另外一个本地线程变量myNode的locked保存本身的状态,true:正在申请锁或已申请到锁;false:已释放锁。每个线程通过监视自身的前驱状态,自旋等待锁被释放。释放锁时,把自身myNode的locked设为false,使后继线程获的锁,并回收前驱的QNode,等待下次使用,因为自身的QNode可能被tail获后继引用,而前驱不再使用老的QNode。

 

该算法也是空间有效的,如果有n个线程,L个锁,每个线程每次只获取一个锁,那么需要的存储空间是O(L+n),n个线程有n个myNode,L个锁有L个tail。这种算法有一个缺点是在NUMA系统架构下性能表现很差,因为它自旋的locked是前驱线程的,如果内存位置较远,那么性能会受到损失。但是在SMP这种cache一致性的系统架构上表现良好。

 

下一章会整理MCSLock,它更适用于NUMA架构。

 

5
0
分享到:
评论
1 楼 fei33423 2014-06-12  
楼主的这个实现蛮难理解.
下面这篇文章的里的实现CLH Lock比较简单
[转]自旋锁、排队自旋锁、MCS锁、CLH锁http://blog.csdn.net/fei33423/article/details/30316377

相关推荐

    自旋锁公平性的三种实现代码下载

    自旋锁是多线程编程中的一个重要概念,用于在共享资源上实现轻量级的同步。...- 锁的释放过程:如何通知等待线程锁已可用。 请务必仔细研究这些代码,它们提供了实践自旋锁公平性策略的宝贵资料。

    Lock详解.pdf

    1. **可重入性**:ReentrantLock允许一个线程多次获取同一锁,即在持有锁的情况下可以再次获取,这与synchronized的可重入性相同。 2. **公平性与非公平性**:ReentrantLock可以通过构造函数参数选择是否为公平锁。...

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

    - 可以通过tryLock尝试获取锁,增加灵活性。 - 提供更精细的锁释放控制,避免异常导致的死锁。 #### 5. 认识AQS - **AQS内部实现**: - 通过`state`字段记录锁的状态,使用CAS操作进行原子更新。 - 通过FIFO...

    ReentrantLock流程浅析

    - **获取锁**:线程尝试获取锁,首先检查state是否为0,如果是则成功获取(无锁状态),并更新state为1,表示锁被占用。否则,线程将变为等待状态,加入到等待队列。 - **入队**:新到来的线程在等待队列尾部插入...

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

    2. `ReentrantLock` 的 `lock()` 方法:加锁的基本过程包括三个步骤:(1)线程尝试竞争锁;(2)竞争锁失败,线程封装成节点加入CLH双向链表阻塞队列;(3)线程进行自旋,尝试竞争锁,竞争锁失败,阻塞排队的线程...

    第10讲 如何保证集合是线程安全的 ConcurrentHashMap如何实现高效地线程安全1

    ConcurrentHashMap的高效实现还依赖于许多并发控制技术,比如无锁数据结构(Lock-Free)、自旋锁(Spin Lock)、循环等待检测(CLH Lock)以及Compare and Swap (CAS)操作。这些技术使得ConcurrentHashMap在不牺牲太...

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

    当线程调用ReentrantLock的`lock()`方法时,实际是调用了`FairSync`类中的`lock()`方法,这个方法最终会调用`acquire(1)`。这里的数字1表示设置锁的状态,对于独占锁来说,初始状态为0,每次获取锁会使状态加1,...

    多线程J.U.C框架(完整)

    如果不能获取,则将线程加入等待队列,并通过内部的`CLHLock`算法实现线程的排队等待。 ##### 共享模式 共享模式与独占模式类似,但允许多个线程同时获取同步状态。在共享模式下,AQS通过`tryAcquireShared`方法...

    JavaMagic:我在哪里进行有关Java新功能的研究

    相比于多线程防止CPU空转Quine 不通过读源文件的方式输出程序源代码本身Vertx 基于netty的NIO实现的小清新WebServer和HttpClientReentrantReadWriteLock的用例以及实现写锁降级为读锁实现三种自旋锁SpinLock直接基于...

    Java锁之自旋锁详解

    3. CLHLock(Craig, Landin, and Hagersten Lock)和MCSLock(Mellor-Crummey and Scott Lock):这两种自旋锁是基于链表结构的公平锁,它们将等待线程组织成一个队列,并使用`AtomicReferenceFieldUpdater`等原子...

    7 AQS源码分析.docx

    例如,在`ReentrantLock`的实现中,`lock()`方法的非公平版本会尝试直接获取锁,如果失败则会进入队列等待。 对于锁的升级过程,Java的`synchronized`关键字在低并发情况下使用的是偏向锁,当只有一个线程访问同步...

    The java.util.concurrent Synchronizer Framework

    - **排队机制**:AQS内部使用CLH锁队列(Craig-Landin-Hagersten lock queue)作为线程等待队列的基础结构。每个等待同步状态改变的线程会被封装成一个节点(`Node`),并加入到队列中。当线程获得同步状态后,会从...

    笔记2232 真的非常不错

    13. **ReentrantLock的实现**:基于AQS(AbstractQueuedSynchronizer),使用CLH锁(自旋锁)维护一个双向队列,线程通过不断轮询前一个节点状态等待锁的释放。 14. **RDB快照**:Redis使用bgSAVE命令创建子进程...

Global site tag (gtag.js) - Google Analytics