# JAVA并发编程(三)-CAS和原子变量
## CAS介绍
CAS(compare and swap)是对一种处理器指令的称呼,java中很多类的实现会借助CAS.CAS可以将read-modify-write和check-and-act操作转换为原子操作,类似于count++这种操作,使用CAS转换为if-then-act操作,用伪代码来描述可以这样描述
```java
public boolean CAS(Variable V,Object A,Object B)
if(A == V.getValue()){
V.set(B);
return true;
}
return false;
```
CAS是一个原子的if-then-act操作,假设一个线程执行CAS操作的时候,变量V的当前值和客户请求CAS提供的变量值是相等的,那么就说明其他线程没有修改过变量V的值,那么当前线程就会抢先将变量V的值修改为B值,其他线程的更新请求将会失败,失败的线程可以选择在此尝试,直到成功.
```java
public class CASBasedCounter {
/**
* 这里使用AtomicLongFieldUpdater只是为了便于讲解和运行该实例,
* 实际上更多的情况是我们不使用AtomicLongFieldUpdater,而是使用
* java.util.concurrent.atomic包下的其他更为直接的类。
*/
private final AtomicLongFieldUpdater<CASBasedCounter> fieldUpdater;
private volatile long count;
public CASBasedCounter() throws SecurityException, NoSuchFieldException {
fieldUpdater = AtomicLongFieldUpdater.newUpdater(CASBasedCounter.class,
"count");
}
public static void main(String[] args) throws Exception {
final CASBasedCounter counter = new CASBasedCounter();
Thread t;
Set<Thread> threads = new HashSet<Thread>();
for (int i = 0; i < 20; i++) {
t = new Thread(new Runnable() {
@Override
public void run() {
Tools.randomPause(50);
counter.increment();
}
});
threads.add(t);
}
for (int i = 0; i < 8; i++) {
t = new Thread(new Runnable() {
@Override
public void run() {
Tools.randomPause(50);
Debug.info(String.valueOf(counter.vaule()));
}
});
threads.add(t);
}
// 启动并等待指定的线程结束
Tools.startAndWaitTerminated(threads);
Debug.info("final count:" + String.valueOf(counter.vaule()));
}
public long vaule() {
return count;
}
public void increment() {
long oldValue;
long newValue;
do {
oldValue = count;// 读取共享变量当前值
newValue = oldValue + 1;// 计算共享变量的新值
} while (/* 调用CAS来更新共享变量的值 */!compareAndSwap(oldValue, newValue));
}
/*
* 该方法是个实例方法,且共享变量count是当前类的实例变量,因此这里我们没有必要在方法参数中声明一个表示共享变量的参数
*/
private boolean compareAndSwap(long oldValue, long newValue) {
boolean isOK = fieldUpdater.compareAndSet(this, oldValue, newValue);
return isOK;
}
```
CAS只能保证变量更新操作的原子性,不保证可见性,还是需要volatile来保证可见性.
CAS也有可能会产生这种场景,原始值是A,线程T1将值改成B,线程T2将值改成A,此时线程T3获取到的值是第一次的A,但是他还是认为是最新值,可以成功去更新。这就是A->B->A问题,有些业务场景下是不可接受这种问题的,如果这样可以加上一个版本号或者时间戳,每个值更新时将版本号加1,根据值+版本号来确定是否是最新值。AtomicStampedReference 类就是根据这种思想产生的类。
## 原子变量类
| 分组 | 类名 |
| ---------- | ------------------------------------------------------------ |
| 基础数据型 | AtomicInteger、AtomicLong、AtomicBoolean |
| 数组型 | AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray |
| 字段更新器 | AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater |
| 引用型 | AtomicReference、AtomicStampedReference、AtomicMarkableReference |
例如AtomicInteger继承与Number类,里面使用了volatile修饰了value变量,使用get()方法相当于读取一个volatile变量,而使用incrementAndGet()方法相当于自增,底层还是通过CAS方式来实现的
```java
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
```
```java
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
```
JAVA并发编程(三)-CAS和原子变量