天天看点

Java并发-CAS操作(关于可见性的思考)

CAS

CAS即CompareAndSwap,比较并交换,是一种保证原子性的操作。有三个操作数,分别是内存地址,旧的预期值,和要替换的新值。

通过比较内存中的值是否和预期值相同,相同则将内存中的值和新值交换。

CAS存在的问题

  1. ABA。根据具体场景看是否会影响执行的结果。
  2. 自旋长时间消耗cpu资源
  3. 只能对一个共享变量操作

CAS可见性

这个其实是我想记录下来的。通过阅读《Java并发编程之美》,在看到原子操作这里,存在疑问,cas能够做到内存可见性吗?阅读了一些博客看到有的说能保证,但还是心存疑惑,于是写了段代码来验证。

public class Ex003 {
    int flag=1;
    static Unsafe unsafe;
    static long fieldOffset;
    static {
        Class<Unsafe> unsafeClass = Unsafe.class;
        try {
            //通过反射获取unsafe
            Field field = unsafeClass.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe=(Unsafe) field.get(null);

            fieldOffset = unsafe.objectFieldOffset(Ex003.class.getDeclaredField("flag"));
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Ex003 ex003 = new Ex003();

        new Thread(new Runnable() {
            int i=0;
            @Override
            public void run() {

                while (ex003.flag==1){
                    //flag变量声明为对象,防止值传递生成副本,保证cas操作的内存地址存的是flag对象
                    i++;
                    //println方法中包含同步代码块,会导致flag值受到synchronized的可见性的影响
                    //System.out.println(i);
                }
            }
        }).start();

        Thread.sleep(1000);
        ex003.changFlag();

    }
    void changFlag(){
        boolean b = unsafe.compareAndSwapInt(this, fieldOffset, 1, 0);
        System.out.println(b+" "+this.flag);
    }


}
           

执行结果是,在打印出"true 0"后,程序依然在运行。而将flag添加volatile修饰后,打印出语句后程序终止。

对于我的理解是:书上说cas的原子操作是由cpu级别的锁来实现的,而他的这个可见性应该是对于多个cpu之间能够保证修改了变量的值对其他cpu是可见的。

而我们常说的Java并发中的内存可见性指的是JMM的可见性,所以cas对共享变量的修改对其他线程是不可见的。

写在最后

关于可见性是我在准备记录cas这个点的时候的突发奇想,然后写下了我的思考于想法。如果大家有什么想法欢迎评论留言,一起探讨。