什么是CAS机制?(进阶篇)

转载自  永远爱大家的   程序员小灰



这一期我们来深入介绍之前遗留的两个问题:

  1. Java当中CAS的底层实现

  2. CAS的ABA问题和解决方法















首先看一看AtomicInteger当中常用的自增方法 incrementAndGet:


public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
private volatile int value;
public final int get() {
return value;
}

这段代码是一个无限循环,也就是CAS的自旋。循环体当中做了三件事:
1.获取当前值。
2.当前值+1,计算出目标值。
3.进行CAS操作,如果成功则跳出循环,如果失败则重复上述步骤。


这里需要注意的重点是 get 方法,这个方法的作用是获取变量的当前值。


如何保证获得的当前值是内存中的最新值呢?很简单,用volatile关键字来保证。有关volatile关键字的知识,我们之前有介绍过,这里就不详细阐述了。









接下来看一看compareAndSet方法的实现,以及方法所依赖对象的来历:


compareAndSet方法的实现很简单,只有一行代码。这里涉及到两个重要的对象,一个是unsafe,一个是valueOffset


什么是unsafe呢?Java语言不像C,C++那样可以直接访问底层操作系统,但是JVM为我们提供了一个后门,这个后门就是unsafe。unsafe为我们提供了硬件级别的原子操作


至于valueOffset对象,是通过unsafe.objectFieldOffset方法得到,所代表的是AtomicInteger对象value成员变量在内存中的偏移量。我们可以简单地把valueOffset理解为value变量的内存地址。


我们在上一期说过,CAS机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B


而unsafe的compareAndSwapInt方法参数包括了这三个基本元素:valueOffset参数代表了V,expect参数代表了A,update参数代表了B。


正是unsafe的compareAndSwapInt方法保证了Compare和Swap操作之间的原子性操作。









什么是ABA呢?假设内存中有一个值为A的变量,存储在地址V当中。






此时有三个线程想使用CAS的方式更新这个变量值,每个线程的执行时间有略微的偏差。线程1和线程2已经获得当前值,线程3还未获得当前值。





接下来,线程1先一步执行成功,把当前值成功从A更新为B;同时线程2因为某种原因被阻塞住,没有做更新操作;线程3在线程1更新之后,获得了当前值B。





再之后,线程2仍然处于阻塞状态,线程3继续执行,成功把当前值从B更新成了A。





最后,线程2终于恢复了运行状态,由于阻塞之前已经获得了“当前值”A,并且经过compare检测,内存地址V中的实际值也是A,所以成功把变量值A更新成了B。





这个过程中,线程2获取到的变量值A是一个旧值,尽管和当前的实际值相同,但内存地址V中的变量已经经历了A->B->A的改变。










当我们举一个提款机的例子。假设有一个遵循CAS原理的提款机,小灰有100元存款,要用这个提款机来提款50元。



由于提款机硬件出了点小问题,小灰的提款操作被同时提交两次,开启了两个线程,两个线程都是获取当前值100元,要更新成50元。


理想情况下,应该一个线程更新成功,另一个线程更新失败,小灰的存款只被扣一次。


线程1首先执行成功,把余额从100改成50。线程2因为某种原因阻塞了。这时候,小灰的妈妈刚好给小灰汇款50元

线程2仍然是阻塞状态,线程3执行成功,把余额从50改成100。

线程2恢复运行,由于阻塞之前已经获得了“当前值”100,并且经过compare检测,此时存款实际值也是100,所以成功把变量值100更新成了50。



这个举例改编自《java特种兵》当中的一段例子。原本线程2应当提交失败,小灰的正确余额应该保持为100元,结果由于ABA问题提交成功了。









什么意思呢?真正要做到严谨的CAS机制,我们在Compare阶段不仅要比较期望值A和地址V中的实际值,还要比较变量的版本号是否一致。


我们仍然以最初的例子来说明一下,假设地址V中存储着变量值A,当前版本号是01。线程1获得了当前值A和版本号01,想要更新为B,但是被阻塞了。




这时候,内存地址V中的变量发生了多次改变,版本号提升为03,但是变量值仍然是A。



随后线程1恢复运行,进行Compare操作。经过比较,线程1所获得的值和地址V的实际值都是A,但是版本号不相等,所以这一次更新失败。





在Java当中,AtomicStampedReference类就实现了用版本号做比较的CAS机制。








1. Java语言CAS底层如何实现?

利用unsafe提供了原子性操作方法。


2. 什么是ABA问题?怎么解决?

当一个值从A更新成B,又更新会A,普通CAS机制会误判通过检测。

利用版本号比较可以有效解决ABA问题。




—————END—————


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

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

相关文章

c++ 前缀 变量命名_前缀命名

c 前缀 变量命名如果您是第一次查看Takes或Cactoos的源代码,很可能会像其他命名约定一样被命名约定触发,这意味着大多数类名都有两个字母的前缀: BkSafe , RqFake , RsWithStatus , TkGzip等。 老实说&…

jvm(8)-虚拟机字节码执行引擎

【0】README0.1)本文转自 “深入理解jvm”,旨在学习 虚拟机字节码执行引擎 的基础知识;【1】概述1)物理机和虚拟机的执行引擎: 物理机的执行引擎是直接建立在处理器,硬件,指令集和操作系统层面上…

什么是大数据

转载自 玻璃猫 程序员小灰大数据是具有海量、高增长率和多样化的信息资产,它需要全新的处理模式来增强决策力、洞察发现力和流程优化能力。Big data is high volume, high velocity, and/or high variety information assets that require new forms of processing…

java 记录考勤记录_Java 14:记录

java 记录考勤记录Java 14是在几周前问世的,它引入了Record类型,它是一个不变的数据载体类,旨在容纳一组固定的字段。 请注意,这是一种预览语言功能 ,这意味着必须使用--enable-preview标志在Java编译器和运行时中显式…

漫画:什么是HashMap

转载自 玻璃猫 程序员小灰众所周知,HashMap是一个用于存储Key-Value键值对的集合,每一个键值对也叫做Entry。这些个键值对(Entry)分散存储在一个数组当中,这个数组就是HashMap的主干。 HashMap数组每一个元素的初始值都…

jvm(10)-早期(编译期)优化

【0】README 0.1)本文部分文字描述转自 “深入理解jvm”,旨在学习 早期(编译期)优化 的基础知识; 0.2)本文部分文字描述转自: http://www.cnblogs.com/zhouyuqin/p/5223180.html 【1】概述 …

etl介绍与etl工具比较_ETL万岁

etl介绍与etl工具比较提取转换负载是从一个数据系统中提取数据并加载到另一个数据系统中的过程。 涉及的数据系统称为源系统和目标系统。 来自源系统的数据形状与目标系统不匹配,因此需要进行一些转换以使其兼容,该过程称为Transformation 。 转换是由m…

漫画:高并发下的HashMap

转载自 玻璃猫 程序员小灰上一期我们介绍了HashMap的基本原理, 这一期我们来讲解高并发环境下,HashMap可能出现的致命问题。HashMap的容量是有限的。当经过多次元素插入,使得HashMap达到一定饱和度时,Key映射位置发生冲突的几率会…

jvm(11)-晚期(运行期)优化

【0】README 0.1)本文部分文字描述转自 “深入理解 jvm”,旨在学习 晚期(运行期)优化 的基础知识; 【1】概述 1)即时编译器(JITjust in time compiler)定义:为了提高…

java 新功能_Java 14的新功能

java 新功能2020年3月17日,Oracle发布了名为Java 14的Java新版本,其中包括许多新功能,工具,安全性,调试和更新的文档方面的改进。 但是,Oracle还向您提供Java的较旧版本,因为它具有向后兼容性&a…

漫画:什么是ConcurrentHashMap

转载自 玻璃猫 程序员小灰————————————————————————前两期我们讲解了HashMap的基本原理,以及高并发场景下存在的问题。没看过的小伙伴可以点击下面链接:漫画:什么是HashMap? 漫画:高并发下的H…

jvm(12)-java内存模型与线程

【0】README0.1)本文部分文字描述转自“深入理解jvm”,旨在学习“java内存模型与线程” 的基础知识;【1】概述1)并发处理的广泛应用是使得 Amdahl 定律代替摩尔定律称为计算机性能发展源动力的根本原因;2)A…

junit mockito_从工作中清除代码–使用JUnit 5,Mockito和AssertJ编写可执行规范

junit mockito可执行规范是也可以用作设计规范的测试。 通过启用通用语言(在DDD世界中,这也称为无处不在的语言 ),它们使技术和业务团队能够进入同一页面。 它们充当代码的未来维护者的文档。 在本文中,我们将看到一种…

漫画:什么是红黑树

转载自 玻璃猫 程序员小灰————————————二叉查找树(BST)具备什么特性呢?1.左子树上所有结点的值均小于或等于它的根结点的值。 2.右子树上所有结点的值均大于或等于它的根结点的值。 3.左、右子树也分别为二叉排序树。下图中这棵树…

jvm(13)-线程安全与锁优化

【0】README 0.1)本文部分文字转自“深入理解jvm”, 旨在学习 线程安全与锁优化 的基础知识; 0.2)本文知识对于理解 java并发编程非常有用,个人觉得,所以我总结的很详细; 【1】概述 【2】线程安…

apache ignite_Kubernetes集群上的Apache Ignite和Spring第3部分:测试应用程序

apache ignite在上一个博客中,我们为Ignite应用程序创建了Kubernetes部署文件。 在此博客上,我们将在Kubernetes上部署Ignite应用程序。 我将在此使用minikube。 让我们先构建 mvn clean install 我将创建一个简单的Docker映像,因此需要Doc…

什么是AES算法?(整合版)

转载自 玻璃猫 程序员小灰 假设有一个发送方在向接收方发送消息。如果没有任何加密算法,接收方发送的是一个明文消息:“我是小灰” 如果消息被中间人截获到,即使中间人无法篡改消息,也可以窥探到消息的内容,从而暴露了…

soapui 测试soap_使用SoapUI调用不同的安全WCF SOAP服务-基本身份验证,第二部分

soapui 测试soap在本系列的第一篇文章中,我们创建了一个基本的身份验证服务,以使用SoapUI进行调用。 因此,在第二篇文章中,我们将逐步演示如何使用此工具成功调用这种服务。 使用SoapUI的1-Basic WCF SOAP –创建新的SOAP项目 首…

如何获得即时编译器(JIT)的汇编代码(linux环境下)

【0】README0.1)本文主要解决如何在linux下获取即时编译器的汇编代码问题;0.2)本文部分内容转自:http://psy-lob-saw.blogspot.jp/2013/01/java-print-assembly.html1)给定java源代码// 单例模式(分析volat…

漫画:什么是SHA系列算法

转载自 玻璃猫 程序员小灰 SHA-1 SHA-1算法可以从明文生成160bit的信息摘要,示例如下: 给定明文:abcd SHA-1摘要:81FE8BFE87576C3ECB22426F8E57847382917ACF SHA-1 与 MD5的主要区别是什么呢? 1.摘要长度不同。 …