Java AQS 核心数据结构-CLH 锁及优化

Java AQS 核心数据结构-CLH 锁

什么是CLH锁

CLH 锁是对自旋锁的一种改进,有效的解决了以上的两个缺点。

  • 第一个是锁饥饿问题。在锁竞争激烈的情况下,可能存在一个线程一直被其他线程”插队“而一直获取不到锁的情况。
  • 第二是性能问题。在实际的多处理上运行的自旋锁在锁竞争激烈时性能较差。

首先它将线程组织成一个队列,保证先请求的线程先获得锁,避免了饥饿问题。其次锁状态去中心化,让每个线程在不同的状态变量中自旋,这样当一个线程释放它的锁时,只能使其后续线程的高速缓存失效,缩小了影响范围,从而减少了 CPU 的开销。

CLH 锁数据结构很简单,类似一个链表队列,所有请求获取锁的线程会排列在链表队列中,自旋访问队列中前一个节点的状态。当一个节点释放锁时,只有它的后一个节点才可以得到锁。CLH 锁本身有一个队尾指针 Tail,它是一个原子变量,指向队列最末端的 CLH 节点。每一个 CLH 节点有两个属性:所代表的线程和标识是否持有锁的状态变量。当一个线程要获取锁时,它会对 Tail 进行一个 getAndSet 的原子操作。该操作会返回 Tail 当前指向的节点,也就是当前队尾节点,然后使 Tail 指向这个线程对应的 CLH 节点,成为新的队尾节点。入队成功后,该线程会轮询上一个队尾节点的状态变量,当上一个节点释放锁后,它将得到这个锁。

下面用图来展示 CLH 锁从获取到释放锁的全过程。

图片

  1. CLH 锁初始化时会 Tail 会指向一个状态为 false 的空节点,如图1所示。
  2. 当 Thread 1(下称 T1)请求获取锁时,Tail 节点指向 T1 对应的节点,同时返回空节点。T1 检查到上一个节点状态为 false,就成功获取到锁,可以执行相应的逻辑了,如图2所示。
  3. 当 Thread 2(下称 T2)请求获取锁时,Tail 节点指向 T2 对应的节点,同时返回 T1 对应的节点。T2检查到上一个节点状态为 True,无法获取到锁,于是开始轮询上一个节点的状态,如图3所示。
  4. 当 T1 释放锁时,会将状态变量置为 False,如图4所示。
  5. T2 轮询到检查到上一个节点状态变为 False,则获取锁成功,如图5所示。

CLH 锁 Java 实现解析

图片

1、节点中的状态变量为什么用 volatile 修饰?可以不用 volatile 吗?

使用 volatile 修饰状态变量不是为了利用 volatile 的内存可见性,因为这个状态变量只会被持有该状态变量的线程写入,只会被队列中该线程的后驱节点对应的线程读,而且后者会轮询读取。因此,可见性问题不会影响锁的正确性。以上面的例子为例,T2 会不断轮询T1的状态变量,T1 将它的状态变更为 False 时 T2 没有立即感知也没有关系。该状态变量最终会写回内存并被 T2 终感知到变更后的值。

但要实现一个可以在多线程程序中正确执行的锁,还需要解决重排序问题。在《Java 并发编程实战》一书对于重排序问题是这么描述的:在没有同步的情况下,编译器、处理器以及运行时等都可能对操作的执行顺序进行一些意想不到的调整。在缺乏足够同步的多线程程序中,要想对内存操作的执行顺序进行判断,几乎无法得到正确的结论。对于 Java synchronized 关键字提供的内置锁(又叫监视器),Java Memory Model(下称 JMM)规范中有一条 Happens-Before(先行发生)规则:“一个监视器锁上的解锁发生在该监视器锁的后续锁定之前”,因此 JVM 会保证这条规则成立。

而自定义互斥锁就需要自己保证这一规则的成立,因此上述代码通过 volatile 的 Happens-Before(先行发生)规则来解决重排序问题。JMM 的 Happens-Before(先行发生)规则有一条针对 volatile 关键字的规则:“volatile 变量的写操作发生在该变量的后续读之前”。

2、CLH 锁是一个链表队列,为什么 Node 节点没有指向前驱或后继指针呢?

CLH 锁是一种隐式的链表队列,没有显式的维护前驱或后继指针。因为每个等待获取锁的线程只需要轮询前一个节点的状态就够了,而不需要遍历整个队列。在这种情况下,只需要使用一个局部变量保存前驱节点,而不需要显式的维护前驱或后继指针。

3、this.node.set(new Node()) 这行代码有何意义?

如果没有这行代码,Node 可能被复用,导致死锁,如下图所示:

图片

CLH 优缺点分析

CLH 锁作为自旋锁的改进,有以下几个优点:

  1. 性能优异,获取和释放锁开销小。CLH 的锁状态不再是单一的原子变量,而是分散在每个节点的状态中,降低了自旋锁在竞争激烈时频繁同步的开销。在释放锁的开销也因为不需要使用 CAS 指令而降低了。
  2. 公平锁。先入队的线程会先得到锁。
  3. 实现简单,易于理解。
  4. 扩展性强。下面会提到 AQS 如何扩展 CLH 锁实现了 j.u.c 包下各类丰富的同步器。

当然,它也有两个缺点:第一是因为有自旋操作,当锁持有时间长时会带来较大的 CPU 开销。第二是基本的 CLH 锁功能单一,不改造不能支持复杂的功能。

AQS 对 CLH 队列锁的改造

针对 CLH 的缺点,AQS 对 CLH 队列锁进行了一定的改造。

针对第一个缺点,AQS 将自旋操作改为阻塞线程操作

针对第二个缺点,AQS 对 CLH 锁进行改造和扩展,原作者 Doug Lea 称之为“CLH 锁的变体”。下面将详细讲 AQS 底层细节以及对 CLH 锁的改进。

AQS 中的对 CLH 锁数据结构的改进主要包括三方面:扩展每个节点的状态显式的维护前驱节点和后继节点以及诸如出队节点显式设为 null辅助 GC 的优化。正是这些改进使 AQS 可以支撑 j.u.c 丰富多彩的同步器实现。

扩展每个节点的状态

volatile int waitStatus;

AQS 同样提供了该状态变量的原子读写操作,但和同步器状态不同的是,节点状态在 AQS 中被清晰的定义,如下表所示:

状态名描述
SIGNAL表示该节点正常等待
PROPAGATE应将 releaseShared 传播到其他节点
CONDITION该节点位于条件队列,不能用于同步队列节点
CANCELLED由于超时、中断或其他原因,该节点被取消
显式的维护前驱节点和后继节点

上文我们提到在原始版本的 CLH 锁中,节点间甚至都没有互相链接。但是,通过在节点中显式地维护前驱节点,CLH 锁就可以处理“超时”和各种形式的“取消”:如果一个节点的前驱节点取消了,这个节点就可以滑动去使用前面一个节点的状态字段。对于通过自旋获取锁的 CLH 锁来说,只需要显式的维护前驱节点就可以实现取消功能,如下图所示:

volatile Node prev;
volatile Node next;

图片

是在 AQS 的实现稍有不同。因为 AQS 用阻塞等待替换了自旋操作,线程会阻塞等待锁的释放,不能主动感知到前驱节点状态变化的信息。AQS 中显式的维护前驱节点和后继节点,需要释放锁的节点会显式通知下一个节点解除阻塞,如下图所示,T1 释放锁后主动唤醒 T2,使 T2 检测到锁已释放,获取锁成功。

图片

其中需要关注的一个细节是:由于没有针对双向链表节点的类似 compareAndSet 的原子性无锁插入指令,因此后驱节点的设置并非作为原子性插入操作的一部分,而仅是在节点被插入后简单地赋值。在释放锁时,如果当前节点的后驱节点不可用时,将从利用队尾指针 Tail 从尾部遍历到直到找到当前节点正确的后驱节点。

辅助 GC

JVM 的垃圾回收机制使开发者无需手动释放对象。但在 AQS 中需要在释放锁时显式的设置为 null,避免引用的残留,辅助垃圾回收。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/484653.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

M/M/m排队模型 (单队列多服务台并联服务模型)数学建模: 基于生灭过程的理论计算和基于事件推进的Matlab模拟仿真思路

M/M/m排队模型 (单队列多服务台并联服务模型) 数学建模: 基于生灭过程的理论计算和基于事件推进的Matlab模拟仿真思路 原创文章,转载文章请注明出处:©️Sylvan Ding 🎉🎉🎉 摘要 本文研究M/M/m单队列多服务台并…

用物理学来为生命技术及制度的演化找出规律-读《为什么世界不会失控》

来源:混沌巡洋舰物理学的规律是最底层的最永恒的,但能够对抗变化,维持自身结构的活系统,其规律则是最丰富的。“The physics of life”这本书,则是用能量的产生和耗散为视角,解释一切活系统,包括…

collection 多态 会自动转型为子类 继承多态需要显示转型

转载于:https://www.cnblogs.com/classmethond/p/10057670.html

深入地下,实地探访!不用人挖煤的智能煤矿长什么样?

来源:脑极体未来智能实验室的主要工作包括:建立AI智能系统智商评测体系,开展世界人工智能智商评测;开展互联网(城市)云脑研究计划,构建互联网(城市)云脑技术和企业图谱&a…

这些行业,将率先落地AI芯片 | 星前沿

来源:联想之星广义上,所有面向AI应用的芯片都可以称为AI芯片。目前一般认为是针对AI算法做了特殊加速设计的芯片。现阶段,这些人工智能算法一般以深度学习算法为主,也可以包括其他浅层机器学习算法。人工智能芯片分类一般有按技术…

[NOI2007]社交网络

题目描述 在社交网络(social network)的研究中,我们常常使用图论概念去解释一些社会现象。不妨看这样的一个问题。在一个社交圈子里有n个人,人与人之间有不同程度的关系。我 们将这个关系网络对应到一个n个结点的无向图上&#xf…

vueJs的简单入门以及基础语法

vue的api vue官网API 1基础语法 1-1基本数据绑定 <div id"app">{{ msg }} </div> //script new Vue({el:"#app",//代表vue的范围data:{msg:hello Vue //数据} }) 在这个例子中我们可以进行赋值 var app new Vue(...); app.msg 初探vue; //那…

应用数学的强大威力

来源&#xff1a;数学中国 应用数学思想是科研当中非常重要的一种思维方式以及研究方法。今天我们就借助戴世强教授的三篇有关应用数学的文章来详细了解一下这些问题&#xff1a;什么是“应用数学思维”&#xff1f;我们如何在研究中使用应用数学思维&#xff1f;应用数学思维…

正则化实现降噪,分别使用最小二乘、定步长梯度下降和回溯法的梯度下降求解最优解

正则化实现降噪&#xff0c;分别使用最小二乘、定步长梯度下降和回溯法的梯度下降求解最优解 原创文章&#xff01;转载需注明来源&#xff1a;©️ Sylvan Ding’s Blog ❤️ 实验目的 参考 INTRODUCTION TO NONELINEAR OPTIMIZATION. Amir Beck. 2014 的 3.4 Denoising …

双向 RNN

1.基本理论 双向 RNN 结合时间上从序列起点开始移动的 RNN 和另一个时间上从序列末尾开始移动的 RNN 2.逻辑图 其中 h (t) 代表通过时间向前移动的子 RNN 的状态&#xff0c;g (t) 代表通过时间向后移动的子 RNN 的状态 允许输出单元 o (t) 能够计算同时依赖于过去和未来且对时…

Hadoop伪分布式配置和搭建,hadoop单机安装,wordcount实例测试,hadoop安装java目录怎么找,问题及问题解决方法

Hadoop伪分布式配置和搭建&#xff0c;hadoop单机安装&#xff0c;wordcount实例测试&#xff0c;hadoop安装java目录怎么找&#xff0c;问题及问题解决方法 环境说明 系统&#xff1a;ubuntu18.04 主机名&#xff1a;test1 用户名&#xff1a;sylvan hadoop单机安装 换源操作…

AI时代竟有智能化鸿沟,具备哪些条件才能跨过?

来源&#xff1a;CSDN移动互联网“早古”时期&#xff0c;普通人因为收入差距问题而无法做到人手一部智能手机&#xff0c;从而导致数字鸿沟。同样&#xff0c;在当前这个AI&#xff0c;也就是人工智能扮演越来越关键作用的时代&#xff0c;企业也站在了类似的抉择交接线上——…

[js] 渲染树构建、布局及绘制

渲染树构建、布局及绘制 转载于:https://www.cnblogs.com/iiiiiher/p/10065345.html

INTRODUCTION TO NONELINEAR OPTIMIZATION Excise 5.2 Freudenstein and Roth Test Function

Amir Beck’s INTRODUCTION TO NONELINEAR OPTIMIZATION Theory, Algorithms, and Applications with MATLAB Excise 5.2 INTRODUCTION TO NONELINEAR OPTIMIZATION Theory, Algorithms, and Applications with MATLAB. Amir Beck. 2014 本文主要涉及题目(ii)的MATLAB部分&…

石正丽/周鹏团队发表冠状病毒新研究,进一步证明新冠非实验室泄漏

来源&#xff1a;中国生物技术网 新冠病毒SARS-CoV-2造成了近一个世纪以来最具破坏性的大流行COVID-19&#xff0c;而其中尚未解决的科学问题之一是这种病毒的自然来源。近日&#xff0c;来自中科院武汉病毒所、中国科学院大学的研究人员在预印本网站BioRxiv上提交了一篇论文&…

L1正则化降噪,对偶函数的构造,求解含L1正则项的优化问题,梯度投影法

L1正则化降噪&#xff0c;对偶函数的构造&#xff0c;求解含L1正则项的优化问题&#xff0c;梯度投影法 本文主要实现L1正则化降噪&#xff0c;L2 正则化降噪的文章在&#xff1a; https://blog.csdn.net/IYXUAN/article/details/121565229 原创文章&#xff01;转载需注明来源…

HBase之Table.put客户端流程(续)

上篇博文中已经谈到&#xff0c;有两个流程没有讲到。一个是MetaTableAccessor.getRegionLocations&#xff0c;另外一个是ConnectionImplementation.cacheLocation。这一节&#xff0c;就让我们单独来介绍这两个流程。首先让我们来到MetaTableAccessor.getRegionLocations。1.…

普华永道:AI规模化应用,基础知识先行

来源&#xff1a;Forbes作者&#xff1a;Cindy Gordon编译&#xff1a;科技行者人工智能&#xff08;AI&#xff09;是正在改变很多行业的游戏规则。据统计&#xff0c;人工智能有望为2030年的全球经济贡献高达15.7万亿美元&#xff0c;比中国和印度目前的产出之和还要多。其中…

ADMM,ISTA,FISTA算法步骤详解,MATLAB代码,求解LASSO优化问题

ADMM,ISTA,FISTA算法步骤详解&#xff0c;MATLAB代码&#xff0c;求解LASSO优化问题 原创文章&#xff01;转载需注明来源&#xff1a;©️ Sylvan Ding’s Blog ❤️ 实验目的 了解 ADMM, ISTA, FISTA 算法的基本原理、收敛性和复杂度&#xff1b;使用上述三种算法&#…

Spring详解(八)------事务管理

目录 1、事务介绍2、事务的四个特性&#xff08;ACID&#xff09;3、Spring 事务管理的核心接口4、 PlatformTransactionManager 事务管理器5、TransactionStatus 事务状态6、TransactionDefinition 基本事务属性的定义7、Spring 编程式事务和声明式事务的区别 8、不用事务…