CAS算法

Posted kykangyuky

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CAS算法相关的知识,希望对你有一定的参考价值。

概述

CAS的全程是:Compare And Swap(比较并交换),CAS是实现并发计算时常用到的技术,Java并发包中的很多类都使用了CAS技术,如ConcurrentHashMap,AtomicInteger原子操作等

CAS操作涉及到3个操作符:当前内存中的值、预估值、即将修改的新增,当且仅当预估值等于内存中的值的时候,才将新的值保存到内存中,否则什么都不做


作用

CAS可以将比较和交换转换为原子操作,这个原子操作直接由CPU保证,CAS可以保证共享变量赋值时的原子操作


特点

CAS是一种非阻塞算法的实现,它能在不使用锁的情况下实现多线程安全,所以CAS也是一种无锁算法

一个线程失败或挂起并不会导致其他线程也失败或挂起


案例

AtomicInteger的incrementAndGet()源码

  • AtomicInteger底层使用到了Unsafe类,它提供了原子操作,Unsafe类可以直接操作内存空间的值
  • Unsafe的CompareAndSwapInt()方法其实就是CAS方法,它会自旋判断当前内存中的值与预期的值是否相等,如果相等就会把内存中的值用更新值替换,如果不相等则不操作,以此来保证原子性
  • 基于硬件平台的汇编指令,在intel的CPU中,使用的是cmpxchg指令,也就是说CAS靠硬件实现,从而在硬件层面提升效率

优点

  1. 由于CAS是非阻塞的,可以避免优先级倒置和死锁等问题
  2. 性能好,使用无锁的方式,没有锁竞争带来的系统开销,也没有线程间频繁调度带来的开销

缺点

  1. 循环时间长开销大(自旋)

    • 如果CAS失败,会一直进行尝试,如果CAS长时间一直不成功,可能给CPU带来很大的开销
    • 解决方法:破坏调死循环,当超过一定时间或者一定次数时,return退出
  2. 只能保证一个共享变量的原子操作

    • 当对一个共享变量操作时,可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性
    • 解决方法:
      1. 用锁来保证原子性
      2. 把多个共享变量合成一个共享变量来操作
      3. 封装成对象 ;Java1.5开始,JDK提供了AtomicReference类来保证引用对象之前的原子性,可以把多个变量放到一个对象里来进行CAS操作
  3. ABA问题

    • 如果内存地址V初次读取的值是A,并且在准备赋值的时候检查到它的值仍然为A,那我们就能说明它的值没有被其他线程改变过吗?

      如果在这段期间它的值曾经被改称了B,然后又改为A,那CAS就会误认为它从来没有被修改过,这就是ABA问题

    • 解决方法:

      1. Java并发包提供了一个带有标记的原子引用类AtomicStampedReference,它可以通过控制变量值的版本来保证CAS的正确性;即在变量前面添加版本号,每次变量更新的时候都把版本号+1,这样变化过程就变成A-B-A变成1A-2B-3A
      2. 增加时间戳

GUC-1 模拟CAS算法

 

/*
 * 模拟 CAS 算法
 */
public class TestCompareAndSwap {

    public static void main(String[] args) {
        final CompareAndSwap cas = new CompareAndSwap();
        
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                
                @Override
                public void run() {
                    int expectedValue = cas.get();
                    boolean b = cas.compareAndSet(expectedValue, (int)(Math.random() * 101));
                    System.out.println(b);
                }
            }).start();
        }
        
    }
    
}

class CompareAndSwap{
    private int value;
    
    //获取内存值
    public synchronized int get(){
        return value;
    }
    
    //比较
    public synchronized int compareAndSwap(int expectedValue, int newValue){
        int oldValue = value;
        
        if(oldValue == expectedValue){
            this.value = newValue;
        }
        
        return oldValue;
    }
    
    //设置
    public synchronized boolean compareAndSet(int expectedValue, int newValue){
        return expectedValue == compareAndSwap(expectedValue, newValue);
    }
}

 

以上是关于CAS算法的主要内容,如果未能解决你的问题,请参考以下文章

JAVA里的CAS算法简析

Java并发编程之CAS

Java并发编程之CAS

CAS原理

java-CAS无锁算法

CAS算法