CAS(compare and swap):比较并且交换, 用于实现多线程同步的原子性,是一条CPU的并发原语;
CAS算法是Java的一种乐观锁,在原子性操作,自旋锁中都有使用;
原理是:在读取到主内存的数据后,保存该数据,然后对读取主内存的数据进行操作。在操作完毕后写回给主内存的时候,检查现在主内存的数去和开始读取的数据是否相同,如果相同,把操作后的数据写到主内存中,执行成功,否则执行失败,重新执行;
CAS的底层原理:unsafe+自旋锁
1.自旋锁:
2.unsafe类:java语言无法直接访问操作系统的底层,但是通过unsafe可以直接访问;
该类保证了CAS的原子性;调用Unsafe类中的CAS方法,JVM会帮助我们实现出CAS汇编指令,这是一条完全依赖于硬件的功能,通过它能够实现原子操作。由于CAS是一条系统原语,是由若干条指令组成,用于实现某个功能的过程,并且原语的执行必须是连续的,在执行过程中不允许被打断,也就是说CAS是一条CPU的原子指令,所以不会造成数据不一致的问题;
unsafe保证了compareAndSwapInt方法中Compare和Swap操作之间的原子性操作;
CAS底层的代码:
public final int incrementAndGet(){
while(1){
int current = get();
int next = current + 1;
if(compareAndSet(current, next)){
return next;
}
}
}
CAS的缺点:
synchronized能够实现一致性,但是并发量下降;
CAS不加锁能够保证一致性和并发性,但是需要多次比较;
缺点:
1.如果CAS失败,会一直进行尝试和循环,如果长时间执行不成功,可能会给CPU带来很大的开销。
2.而且对于一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作;但是对于多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以使用锁来保证原子性;
3.会面临ABA的问题;
CAS的ABA问题:
例如线程1在内存中取出A, 这时线程2在内存中取出A,并且线程2进行了一系列的操作将值变为B,然后线程2又经过一系列的操作将数据变为A,这个时候线程1进行CAS操作发现内存中仍然是A,然后线程1操作成功;
尽管线程1的CAS操作成功,但是不代表这个过程没有问题;
ABA问题的解决:
新增一种机制,那就是版本号(类似于时间戳);
AtomicStampedReference类;