源码阅读笔记:J.U.C CLH lock

java.utils.concurrency(J.U.C)中提供了一系列对锁的封装,如ReentrantLock、CountDownLatch等,而这其中用到的实现又都基于AbstractQueuedSynchronizer(AQS)。而CLH算法又是AQS实现的基础,所以网上查阅了一些资料,总结在此。

CLH lock is Craig, Landin, and Hagersten (CLH) locks, CLH lock is a spin lock, can ensure no hunger, provide fairness first come first service.The CLH lock is a scalable, high performance, fairness and spin lock based on the list, the application thread spin only on a local variable, it constantly polling the precursor state, if it is found that the pre release lock end spin.

CLH是一种独占、公平锁,可以每个线程总会等到自己能征用锁的机会,并严格按照征用的先后顺序。看到自旋锁(spinlock),指的应该是JDK1.4.2引入的一种锁的优化方式。通常意义上的锁,会使等待该锁的线程进入BLOCK状态,而对于实现于内核线程之上Java线程而言,每次挂起与恢复都需要切换到内核态,这对性能是极大的开销。而自旋锁即是让线程进行忙循环(自旋),直到锁得到释放为止。但由于自旋本身也是对CPU的占用(并不处理任何有用逻辑),并且如果大量线程都长时间处于自旋,反而会带来性能的下降。详细的看看CLH的实现:

public class CLHLock implements Lock {  
    private static class Qnode {
        // volatile保证了可见性
        // 当解锁时,等待中的线程可以立刻获知
        public volatile boolean locked = false; 
    }
    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) {} //spinning, 自旋
    }  

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

从代码可以看出,CLH本质是一个链表,每个线程利用ThreadLocalStorage记录自己以及自己的前驱节点,一直自旋观察着前驱节点的状态,直到前驱节点不再征用(可能也在自旋等待锁,也可能是未释放锁)这个锁为止。这所有的信息通过一个AutomaticReference类型的tail(始终指向最后一个征用锁的线程)来相互传递。当一个新的线程征用CLH锁时,状态变化如下图所示。

CLH lock 示意

可以看出,每个线程都是在一个volatile型的变量上进行自旋,同时共享这个变量的至多只有两个线程——维护这个变量的节点/线程本身以及其后继节点/线程。这样通过保证“竞态尽量少地出现”的方式,性能得到了良好的保证。


参考文献

https://segmentfault.com/a/1190000007094429
http://blog.csdn.net/chenssy/article/details/50245555

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值