JDK 并发 AQS 系列(四)

栏目: Java · 发布时间: 5年前

内容简介:--------------------------------------跟我交流:

自旋锁的不足

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

  • 仅适用于占用时间短、颗粒度很小的情景。

  • 需要硬件级别的原子操作。

  • 它无法保证公平性。

  • 每次读写操作需要同步每个处理器的缓存。

CLH锁

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

核心思想

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

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

  1. 构建怎样的队列&如何构建队列?为了保证公平性,将构建一个FIFO队列,构建的时候主要通过移动尾部节点tail实现队列的排队,每个想获取锁的线程创建一个新节点并通过CAS原子操作将新节点赋予tail,然后让当前线程轮询前一节点的某个状态位,如此就成功构建线程排队队列;

  2. 如何释放队列,执行完线程后只需将当前线程对应的节点状态位置为解锁状态,由于下一节点一直在轮询,可获取到锁。

JDK 并发 AQS 系列(四)
image

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);
    }

}

--------------------------------------

跟我交流:

JDK 并发 AQS 系列(四)

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

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

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

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

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

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

2017文章汇总——JDK源码篇

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

2017文章汇总——Java并发篇


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

代码整洁之道:程序员的职业素养

代码整洁之道:程序员的职业素养

罗伯特·C.马丁 (Robert C.Martin) / 余晟、章显洲 / 人民邮电出版社 / 2016-9-1 / 49.00元

1. 汇聚编程大师40余年编程生涯的心得体会 2. 阐释软件工艺中的原理、技术、工具和实践 3. 助力专业软件开发人员具备令人敬佩的职业素养 成功的程序员在以往的工作和生活中都曾经历过大大小小的不确定性,承受过永无休止的压力。他们之所以能够成功,是因为拥有一个共同点,都深切关注创建软件所需的各项实践。他们将软件开发视为一种需要精雕细琢加以修炼的技艺,他们以专业人士的标准要求自己,......一起来看看 《代码整洁之道:程序员的职业素养》 这本书的介绍吧!

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具