一、什么是CAS
CAS(compareAndSet),比较交换,是一种无锁原子算法。过程是:它包含三个参数(V、E、N),V表示要更新变量的值,E表示预期值,N表示新值。仅当V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。最后CAS返回当前V的真实值。CAS操作时抱着乐观的态度进行的,它总是认为自己可以成功完成操作。
当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其他线程均会失败。失败的线程不会挂起,仅是被告知失败,并且允许再次尝试,当然也允许实现的线程放弃操作。基于这样的原理,CAS操作即使没有使用锁,也可以发现其他线程对当前线程的干扰。
与锁相比,使用CAS会使程序看起来要更复杂一些,但由于其非阻塞,它就不会发生死锁问题,并且线程间的相互影响也非常小。更为重要的是,使用无锁的方式完全没有锁竞争带来的系统开销,也没有线程间的频繁调度带来的开销,因此,它要比基于锁的方式拥有更优越的性能。
简单的说,CAS需要你额外的给出一个期望值,也就是你认为这个变量现在应该是什么样子的,如果变量不是你想象的那样,那说明它已经被其他线程修改过了,你就需要重新读取,再次尝试修改就好。
二、CAS底层原理
这要归功于硬件指令集的发展,实际上,我们可以使用同步将两个操作变成原子的,但是这么做就没有意义了。所以我们只能靠硬件来完成,硬件保证一个语义上看起来需要多次操作的行为只通过一条处理器指令就能完成。常用的这类指令有:
1.测试并设置(Test-and-Set)
2.获取并增加(Fetch-and-Increment)
3.交换(Swap)
4.比较并交换(Compare-and-Swap)
5.加载链接/条件存储(Load-Linked/Store-Condition)
Cpu实现原子指令的2种方式;
1) 通过总线加锁来保证原子性
2)通过缓存锁定来保证原子性
三、作用及优点
CAS无锁,不存在阻塞,提高了效率,CPU吞吐量提高。
四、AtomicInteger原子自增代码演示
package current;
import java.sql.Time;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;
import sun.security.krb5.internal.ASRep;
public class CAS2 {
private static AtomicInteger atomicInteger = new AtomicInteger(100);
private static AtomicStampedReference atomicStampedReference=new AtomicStampedReference(100, 1);
public static void main(String[] args) throws InterruptedException {
Thread t1= new Thread(()->{
System.out.println(atomicInteger.compareAndSet(100, 110));
});
t1.start();
Thread t2 = new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(atomicInteger.compareAndSet(110, 100));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
t2.start();
Thread t3 = new Thread(()->{
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(atomicInteger.compareAndSet(100, 120));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
t3.start();
TimeUnit.SECONDS.sleep(5);
System.out.println("=====================================");
Thread tf1 = new Thread(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(atomicStampedReference.compareAndSet(100, 110, atomicStampedReference.getStamp(), atomicStampedReference.getStamp()+1));
System.out.println(atomicStampedReference.compareAndSet(110, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp()+1));
});
Thread tf2 = new Thread(()->{
int stamp = atomicStampedReference.getStamp();
try {
TimeUnit.SECONDS.sleep(4);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(atomicStampedReference.compareAndSet(100, 120, stamp, stamp+1));
});
tf1.start();
tf2.start();
}
}
CAS A-B-A问题,类AtomicStampedReference A1-B2-A3
五、CAS应用的场景
1.应用于简单的数据计算
2.适合线程冲突少的场景