JDK并发AQS系列(四)

前面说到用自旋方式来获取,能有效避免线程挂起和恢复。但它也有不足之处:

  • 仅适用于占用时间短、颗粒度很小的情景。
  • 需要硬件级别的原子操作。
  • 它无法保证公平性。
  • 每次读写操作需要同步每个处理器缓存

CLH锁

鉴于自旋锁的不足,Craig,Landin, Hagersten发明了CLH锁,用来优化同步带来的花销。

核心思想

CLH锁通过一定的手段将所有线程对某一共享变量轮询竞争转化为一个线程队列且队列中的线程各自轮询自己的本地变量。

这个转化过程有两个要点:

  1. 构建怎样的队列&如何构建队列?为了保证公平性,将构建一个FIFO队列,构建的时候主要通过移动尾部节点tail实现队列的排队,每个想获取锁的线程创建一个新节点并通过CAS原子操作将新节点赋予tail,然后让当前线程轮询前一节点的某个状态位,如此就成功构建线程排队队列;
  2. 如何释放队列,执行完线程后只需将当前线程对应的节点状态位置为解锁状态,由于下一节点一直在轮询,可获取到锁。
JDK并发AQS系列(四)

CLH锁的核心思想貌似是将众多线程长时间对某资源的竞争,通过有序化这些线程转化为只需对本地变量检测。唯一存在竞争的地方就是在入队列之前对尾节点tail的竞争,但竞争的线程的数量已经少了很多,且比起所有线程直接对某资源竞争的轮询次数也减少了很多,节省了很多CPU缓存同步操作,大大提升系统性能,利用空间换取性能。

代码实现

下面提供一个简单的CLH锁实现代码,lock与unlock两方法提供加锁解锁操作,每次加锁解锁必须将一个CLHNode对象作为参数传入,lock方法的for循环是通过CAS操作将新节点插入队列,而while循环则是检测前驱节点的锁状态位,一旦前驱节点锁状态位允许则结束检测让线程往下执行。

解锁操作先判断当前节点是否为尾节点,如是则直接将尾节点置为空,此时说明仅仅只有一条线程在执行,否则将当前节点的锁状态位置为解锁状态。

public class CLHLock {

	private static Unsafe unsafe = null;
	private static final long valueOffset;
	private volatile CLHNode tail;

	public class CLHNode {
		private boolean isLocked = true;
	}

	static {
		try {
			unsafe = getUnsafeInstance();
			valueOffset = unsafe.objectFieldOffset(CLHLock.class.getDeclaredField("tail"));
		} catch (Exception ex) {
			throw new Error(ex);
		}
	}

	public void lock(CLHNode currentThreadNode) {
		CLHNode preNode = null;
		for (;;) {
			preNode = tail;
			if (unsafe.compareAndSwapObject(this, valueOffset, tail, currentThreadNode))
				break;
		}
		if (preNode != null)
			while (preNode.isLocked) {
			}
	}

	public void unlock(CLHNode currentThreadNode) {
		if (!unsafe.compareAndSwapObject(this, valueOffset, currentThreadNode, null))
			currentThreadNode.isLocked = false;
	}

	private static Unsafe getUnsafeInstance()
			throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
		Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe");
		theUnsafeInstance.setAccessible(true);
		return (Unsafe) theUnsafeInstance.get(Unsafe.class);
	}

}

复制代码

————-推荐阅读————

我的开源项目汇总(机器&深度学习、NLP、网络IO、AIML、mysql协议、chatbot)

为什么写《Tomcat内核设计剖析》

我的2017文章汇总——机器学习篇

我的2017文章汇总——Java及中间件

我的2017文章汇总——深度学习篇

我的2017文章汇总——JDK源码

我的2017文章汇总——自然语言处理篇

我的2017文章汇总——Java并发

跟我交流,向我提问

JDK并发AQS系列(四)

欢迎关注:

JDK并发AQS系列(四)

原文 

https://juejin.im/post/5bd10e47e51d457a765bd7a6

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » JDK并发AQS系列(四)

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址