Java CAS:AtomicInteger、AtomicReference、AtomicStampedReference
什么是CAS?
什么是CAS? 即比较并替换,实现并发算法时常用到的一种技术。CAS操作包含三个操作数——内存位置、预期原值及新值。执行CAS操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作。我们都知道,CAS是一条CPU的原子指令(cmpxchg指令),不会造成所谓的数据不一致问题,Unsafe提供的CAS方法(如compareAndSwapXXX)底层实现即为CPU指令cmpxchg。
* CAS
* @param o 包含要修改field的对象
* @param offset 对象中某field的偏移量
* @param expected 期望值
* @param update 更新值
* @return true | false
*/
public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object update);
public final native boolean compareAndSwapInt(Object o, long offset, int expected,int update);
public final native boolean compareAndSwapLong(Object o, long offset, long expected, long update);
AtomicInteger:
AtomicInteger代表着基本类型的原子类:其中有AtomicInteger、AtomicBoolean、AtomicLong等.
其中AtomicInter继承 Number 实现 Serializable接口
应用
方法:
1)public AtomicInteger();//初始化该类。初始值为0
2)public AtomicInteger( int initialValue) //给定初始值的初始化
在程序中需要原子的增加或减少计数器时,使用此类 AtomicInteger
public class Counter{
private AtomicInteger ia = new AtomicInteger();
public void increase(){
ia.getAndIncrement();
}
public int get(){
return ia.get();
}
}
public class Worker extends Thread{
Counter counter;
public Worker( Counter counter){
this.counter = counter;
}
public void run (){
for(int i=0;i<100;i++){
counter.increase();
}
}
}
public class Index{
public static void main(String[] args){
Counter counter = new Counter(0;
Thread t1 = new Worker(counter);
Thread t2 = new Worker(counter);
t1.start();
t2.start();
try{
t1.jion();
t2.jion();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("counter.get() = " + counter.get());
}
}
AtomicReference
上面讲到AtomicInteger对一个共享变量执行操作时,CAS能够保证原子操作,但是对多个共享变量操作时,CAS是无法保证操作的原子性的。
Java从1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,可以把多个变量放在一个对象里来进行CAS操作。
方法:
// 用V来指明应用的类型
public AtomicReference() // 用初始值null创建类AmoticReference的实例。
public AtomicReference( V initialValue) // 用初始initialValue 创建类AtomicReference的实例,
//举例
public class Counter(){
private AtomicReference<Integer> af = new AtomicReference<Integer>(0);
public void increase(){
Integer temp = af.get();
af.comparseAndSet( temp,temp+1);
}
public Integet get(){
return af.get();
}
}
public class Worker extends Thread{
Counter counter;
public Worker( Counter counter){
this.counter = counter;
}
public void run (){
for(int i=0;i<100;i++){
counter.increase();
}
}
}
public class Index{
public static void main(String[] args){
Counter counter = new Counter(0;
Thread t1 = new Worker(counter);
Thread t2 = new Worker(counter);
t1.start();
t2.start();
try{
t1.jion();
t2.jion();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("counter.get() = " + counter.get());
}
}
AtomicStampedReference
是什么?首先需要了解一下ABA问题
ABA问题是指在CAS操作中带来的潜在问题
CAS意思是 compare and swap 或者 compare and set , 对于一个要更新的变量A,我们提供一个它的旧值a 和新值 b ,如果变量A的值等于旧值 那么更新成功, 否则失败。
举例:
比如两个线程
- 线程1 查询A的值为a,与旧值a比较,
- 线程2 查询A的值为a,与旧值a比较,相等,更新为b值
- 线程2 查询A的值为b,与旧值b比较,相等,更新为a值
- 线程1 相等,更新B的值为c
可以看到这样的情况下,线程1 可以正常 进行CAS操作,将值从a变为c 但是在这之间,实际A值已经发了a->b b->a的转换
仔细思考,这样可能带来的问题是,如果需要关注A值变化过程,是会漏掉一段时间窗口的监控
另外:cas本身不是锁,只是cas很容易用来封装成锁,乐观锁只是一种思想,并不是真的有一种锁是乐观的。有些场景是不封装成锁直接使用cas,例如对AtomicInteger的increase,这就是乐观锁思想的实践,所以AtomicInteger.increase才能被称为无锁实现。
问题解决
JDK从1.5开始提供了AtomicStampedReference类来解决ABA问题,具体操作封装在compareAndSet()中。compareAndSet()首先检查当前引用和当前标志与预期引用和预期标志是否相等,如果都相等,则以原子方式将引用值和标志的值设置为给定的更新值。