线程基础知识02-CAS (CompareAndSwap)
1.基本概念
原子性是不可中断的最小操作;在Java中,一般通过加锁或者自旋CAS方式来确保原子操作;
而CAS(compareAnd swap)作为Java中常用的保证原子性的手段,JDK1.5之后就提供了相关的操作类,java.util.concurrent.atomic包中的工具类;
CAS概念:CAS操作需要两个值,旧值(预期的操作之前的值)和新值,先将旧值进行比较,如果旧值没有发生变化,则进行操作,如果旧值变化则不进行任何操作
2.常用来解释CAS的一个案例:
public class CASTest {
private int i = 0;
private AtomicInteger ai = new AtomicInteger();
private static final Integer NUM = 10;
private static CountDownLatch latch = new CountDownLatch(NUM);
public static void main(String[] args) throws InterruptedException {
final CASTest t = new CASTest();
for(int i =0 ; i < NUM ; i++){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for(int j = 0 ; j < 100 ; j++){
t.count();
// t.safeCount();
}
latch.countDown();
}
});
thread.start();
}
latch.await();
System.out.println("======= over ======="+t.i);
}
public void count(){
i ++ ;
}
public void safeCount(){
for(;;){
int i = ai.get();
boolean b = ai.compareAndSet(i, i++);
if( b ){
break ;
}
}
}
}
理想的结果是1000,但是非CAS的结果总是会有差异,而safeCount 采用的是CAS的方式进行原子操作;
3.CAS 操作存在的问题和解决方案
1.ABA问题
问题描述:首先,假设一个变量经过A=>B=>A的变化过程,一个线程进行,获取到操作前的值为A,按照上面的概念这个线程获取到的旧值A,和这个变量进行变化后的现在的新值A是一样的。如果进行CAS自旋操作无疑是有问题的。
解决方案:JDK中atomic包中提供了,AtomicStampedReference 类,提供了更完善的方法。通过增加标志,来对旧值进行判断。
2.循环时间长开销大
问题描述:当一个线程修改的值,被其他线程再次修改后,自旋旧值比对长时间无法获取成功,导致资源的浪费;
解决方案:JVM中提供了puse指令可以解决这种问题;
3.只能保证一个变量的原子操作
问题描述:如题
解决方案:通过将多个变量封装成一个对象,通过atomic包中的AtomicReference类来进行解决;
参考图书《java并发编程艺术》
最后,