CAS 机制 & ABA 问题的理解

1.CAS机制的理解(compare and swap)
涉及多个线程对同一个值进行修改时,我们为了避免值的正确性,保证操作的原子性。这个时候,我们经常使用同步的方式来保证操作的原子性
synchronized(Class.className),这样加锁了之后,我们对某一块区域的代码就能够保证每次只有一个线程对其进行操作,而当其他线程对想要
对这块区域的代码进行操作时,只能等待持有这块区域的线程处理完了之后,释放锁之后,这个线程才有可能获取锁,从BLOCKING阻塞态,变为
进行态,这个过程涉及用户态和内核态的转化,在这个过程中比较耗资源。


这个时候,我们想到能不能用一种轻量级别的处理方式或者用一种乐观的处理方式来处理多个线程对一个值的修改操作,JAVA 提供了java.util.
concurrent AtomicInteger AtomicBoolean AtomicLong 等原子类,这些类采用的CAS机制来处理这种类型的问题,首先,机制中采用 
a.值内存地址 b.期望旧值expect c.更新新值update 三个属性


值100  更新值 101


A线程 100 101  
B线程 100 
首先线程A获取期望的旧值,这个时候,通过内存地址去内存里面获取实际值为100,通过比较实际值和期望值,发现是一样的,那么这个时候,
更新新值为101.A线程完成以后,值内存地址中的值已经是101了,这个时候B线程去进行更新的操作,首先比较expect 值和 内存地址的值不一样,
这个时候发现不一样了,所以,这个时候更新操作会基于新的值来更新期望值,重新进行更新的操作。




2.ABA的理解以及解决方式
 AB:100 更新成50,C:更新为100
A 100 
B 100  BLOCKING
C 更新 为 100 


这个时候A线程首先进行更新操作,成功的进行了更新操作,这个时候将内存地址实际值更新为50,这个时候B线程,还在阻塞状态,C线程进行更新
的操作,首先获取50实际值,然后比较一样,将值更新为100,完成操作,这个时候,B线程获取资源,开始运行态,获取到的期望值是100,然后获
取内存实际值,100,然后进行更新的操作,将值更新为50. 这个过程涉及到了A -> B -> A 的过程。其实B线程获取到的期望值已经和已经目前内存
中的实际值已经不一样了,所以,这个时候,再将值更新为50 已经是不正确了。我们为了区分这种情况,因为数值不能区分 50 和 50 之间有什么
区别,所以,我们使用另外一个属性来解决这个问题,就是版本号。每次更新一个值,我们需要去更新一下这个值的版本号。这样,当我们更新值进行
比较操作的时候,我们需要比较两个属性,一个是值属性,还有一个就是其版本属性,只有当这个两个属性保持一致的时候,我们才进行值更新的操作



在上述例子中,当AB线程获取期望值时,同时也获取到了其版本号 1.0,当A线程完成更新操作时,版本号变为1.1,然后C线程完成更新操作版本号变
为1.2,这个时候值还是100,然后B线程去进行更新的操作的时,发现期望值和实际值是一样的,但是版本号不一样,这个时候,更新操作也是失败的。


在JAVA里面,我们一般是不能直接操作物理内存地址的,但是JAVA开了一个后门,unsafe,可以获取到VALUE的内存地址值。


利弊:我们使用原子类,仅仅能够保证一个数值的原子性,可是当涉及到多个数值的时候,这个时候我们也只能使用同步的方式来处理这个问题。

猜你喜欢

转载自blog.csdn.net/z123271592/article/details/79006134
今日推荐