源码阅读笔记:J.U.C CLH lock
来源:互联网 发布:匠人工坊淘宝 编辑:程序博客网 时间:2024/06/18 06:55
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锁时,状态变化如下图所示。
可以看出,每个线程都是在一个volatile型的变量上进行自旋,同时共享这个变量的至多只有两个线程——维护这个变量的节点/线程本身以及其后继节点/线程。这样通过保证“竞态尽量少地出现”的方式,性能得到了良好的保证。
参考文献
https://segmentfault.com/a/1190000007094429
http://blog.csdn.net/chenssy/article/details/50245555
- 源码阅读笔记:J.U.C CLH lock
- “J.U.C”:CLH队列锁 (r)
- J.U.C之Lock
- 【Java并发编程实战】-----“J.U.C”:CLH队列锁
- 【Java并发编程实战】-----“J.U.C”:CLH队列锁
- J.U.C之AQS:CLH同步队列
- “J.U.C”:锁,lock (r)
- jdk1.8 J.U.C并发源码阅读------ReentrantLock源码解析
- jdk1.8 J.U.C并发源码阅读------ReentrantReadWriteLock源码解析
- jdk1.8 J.U.C并发源码阅读------CountDownLatch源码解析
- jdk1.8 J.U.C并发源码阅读------CyclicBarrier源码解析
- 【死磕Java并发】-----J.U.C之AQS:CLH同步队列
- jdk1.8 J.U.C并发源码阅读------AQS之独占锁的获取与释放
- jdk1.8 J.U.C并发源码阅读------AQS之共享锁的获取与释放
- jdk1.8 J.U.C并发源码阅读------AQS之conditionObject内部类分析
- “J.U.C”:ReentrantLock之二lock方法分析 (r)
- 【Java并发编程实战】-----“J.U.C”:锁,lock
- “J.U.C”:ReentrantLock之二lock方法分析
- 教你如何从Spring官网上查找SpringIOC框架中的beans的配置头文件
- 计算机网络自顶向下方法中文第六版总结(1)
- java service wrapper导致内存剧增直至崩溃
- 【选择排序】
- cmake3.7编译VS2015+opencv3.2的第三方库中dnn库(win7系统64位)
- 源码阅读笔记:J.U.C CLH lock
- 愚公移山,为火星人平反昭雪,挖山不止
- 一个c语言规范引发的一串问题
- 安装配置hive
- PC使用Proxifier设置ss实现应用程序级别代理(测试有效)
- binder再探(1)
- JAVA jdbc 用LIKE模糊搜索内容——prepareStatement LIKE语句
- 401. Binary Watch
- 幻方