深入理解CAS (Compare and swap )
CAS:比较当前工作内存中的值和主内存中的值,如果这个值是期望的,就执行操作,否则就一直循环。
举例对比
不使用CAS,多线程环境不使用原子类保证线程安全i (基本数据类型),加同步锁 synchronized
public class T1 {
volatile int number=0;
//读取
public int getNumber(){
return number;
}
//写入加锁保证原子性
public synchronized void setNumber(){
number ;
}
}
使用CAS,在多线程环境利用原子类 AtomicInteger
保证线程安全i (基本数据类型),类似乐观锁
import java.util.concurrent.atomic.AtomicInteger;
public class T1 {
AtomicInteger atomicInteger=new AtomicInteger();
//读取
public int getAtomicInteger(){
return atomicInteger.get();
}
//写入原子整型类保证原子性
public void setAtomicInteger(){
atomicInteger.incrementAndGet();
}
}
缺点
ABA
问题描述:线程t1将它的值从A变为B,再从B变为A。同时有线程t2要将值从A变为C。但CAS检查的时候会发现没有改变,但是实质上它已经发生了改变 。可能会造成数据的缺失。
解决方法:CAS还是类似于乐观锁,同数据乐观锁的方式给它加一个版本号或者时间戳,如AtomicStampedReference。
自旋消耗资源
问题描述:多个线程争夺同一个资源时,如果自旋一直不成功,将会一直占用CPU。
解决方法:破坏掉while死循环,当超过一定时间或者一定次数时,return退出。JDK8新增的LongAddr和ConcurrentHashMap类似的方法。当多个线程竞争时,将粒度变小,将一个变量拆分为多个变量,达到多个线程访问多个资源的效果,最后再调用sum把它合起来。虽然base和cells都是volatile修饰的,但感觉这个sum操作没有加锁,可能sum的结果不是那么精确。
多变量共享一致性问题
解决方法: CAS操作是针对一个变量的,如果对多个变量操作,
- 可以加锁来解决。
- 封装成对象类解决。