Java并发编程之CAS第三篇-CAS的缺点及解决办法 (2)

在Java中变量的类型分为八大基本类型或八大基本类型的对象类型或者是自定义的对象类型。在并发中,atomicInteger就是基本类型就是int/Integer的原子类。那么自定义的对象怎么实现原子性呢?这就要用到原子引用对象- AtomicReference。

原子引用demo:

我们来模拟凯哥心中女神变化过程(注:女神同时只能存在一个,不能存在多个,要保持单一,原子的)。

在X年之前是刘亦菲,X+N年后是林依晨,现在是佟丽娅了。我们知道,这三个女神都是对象。都有年龄、用户名,是个对象。

创建user对象

Java并发编程之CAS第三篇-CAS的缺点及解决办法

 

她们三个在凯哥心中活动如下:

Java并发编程之CAS第三篇-CAS的缺点及解决办法

 

那么请问在21和23行输入的结果是什么?

Java并发编程之CAS第三篇-CAS的缺点及解决办法

编辑

我们发现在23行依然输出的是林依晨。而不是佟丽娅。为什么呢?分析思路见:《Java并发编程之CAS一理解》篇文章的三:cas代码演示部分。

我们修改之后再来看:

Java并发编程之CAS第三篇-CAS的缺点及解决办法

 

运行结果:

Java并发编程之CAS第三篇-CAS的缺点及解决办法

 

发现心中女神已经更新为佟丽娅了

三:ABA问题解决

ABA问题产生的根本原因是因为:只是线程自己工作空间的变量预期值(副本)和主内存中的值进行了比较。当值相等的时候,就默认没有被其他线程更新过。那么怎么解决这个问题呢?

是不是可以添加一个东西,用来辅助呢?添加一个标记,或者一个版本号,根据版本号+数值来进行判断呢?当然可以了,JDK中也是这么实现的。JDK使用的是时间戳(stamp),而不是我们说的版本号(version)。我们来看看时间戳原子引用(AtomicStampedReference<V>).

我们来看看这个类。

时间戳原子引用demo

先看构造器:

Java并发编程之CAS第三篇-CAS的缺点及解决办法

 

参数说明:

initialRef:初始值

initialStamp:初始值的时间戳

再来看看CompareAndSet方法:

Java并发编程之CAS第三篇-CAS的缺点及解决办法

 

参数说明:

expectedReference:预期值

newReference:更新值

expectedStamp:预期时间戳值

newStamp:更改后时间戳值

我们发现这个AtomicStampedReference类和AtomicReference的方法中的区别就是时间戳原子引用类中的方法都添加了预期的时间戳值和修改后的时间戳的值这两个参数。

我们来看看,使用带有时间戳的原子引用类解决ABA问题的代码:

1:声明共享变量

static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(127,1);

(需要说明,如果用数值做demo的话,主要int的取值范围。如果大于127,就会始终返回false。因为 Integer(128) == Integer(128)返回的是false)

线程一先修改执行一个ABA的过程:

Java并发编程之CAS第三篇-CAS的缺点及解决办法

编辑

执行完成之后,当前的主内存中版本号应该是3了。

我们在用线程2来执行compareAndSet:

Java并发编程之CAS第三篇-CAS的缺点及解决办法

 

此时,在线程2中的版本号:tamp应该是1,但是主内存中的版本号已经是3了。所以执行后返回false.执行不成功的。

我们来看看运行结果和我们预期结果:

Java并发编程之CAS第三篇-CAS的缺点及解决办法

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zypsxw.html