cas-----保证数据一致性,cas操作间的可见性,但不保证JMM内存可见性

先来个示例:

import lombok.Data;

@Data
public class CasTestObjecy {
    private   int value = 0;
}
package com.fen.fou;

import org.springframework.objenesis.instantiator.util.UnsafeUtils;
import sun.misc.Unsafe;

public class CasTest2 {
    static CasTestObjecy t = new CasTestObjecy();

    public static void main(String[] args) throws NoSuchFieldException{
        Unsafe u = UnsafeUtils.getUnsafe();

        long b1 = u.objectFieldOffset(CasTestObjecy.class.getDeclaredField("value"));
        new Thread(()->{
              int a;boolean flag;
              while (flag = (( a =  t.getValue() ) != 5)) ;
            System.out.println(Thread.currentThread().getName() + "-----------------------------------"+a);
            System.out.println(Thread.currentThread().getName() + "-----------------------------------"+flag);

        },"A").start();

        new Thread(()->{
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "-----------------cas---before---------------"+t.getValue());
            u.compareAndSwapInt(t, b1,0,5);
            System.out.println(Thread.currentThread().getName() + "-----------------cas---after---------------"+t.getValue());
         },"B").start();
    }
}

看打印结果,线程B通过cas操作已经把值修改了,但是线程A还处于死循环中

那大家想一下,如果不保证可见性,那大批的cas操作是不是会出现数据不一致的情况

其实一直有个误解,内存可见性是针对JMM而言的,所以上述情况线程A一直获取不到新的值,而cas是cpu执行的时候每次都会去操作对象的内存地址去取值,所以每次取的都是相对而言是最新的

package com.fen.fou;

import org.springframework.objenesis.instantiator.util.UnsafeUtils;
import sun.misc.Unsafe;

public class CasTest2 {
    static CasTestObjecy t = new CasTestObjecy();

    public static void main(String[] args) throws NoSuchFieldException{
        Unsafe u = UnsafeUtils.getUnsafe();

        long b1 = u.objectFieldOffset(CasTestObjecy.class.getDeclaredField("value"));
        new Thread(()->{
              int a;boolean flag;
              while (flag = !u.compareAndSwapInt(t, b1,5,1)) ;
            System.out.println(Thread.currentThread().getName() + "-----------------------------------"+t.getValue());
            System.out.println(Thread.currentThread().getName() + "-----------------------------------"+flag);

        },"A").start();

        new Thread(()->{
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            u.compareAndSwapInt(t, b1,0,5);
         },"B").start();
    }
}

则线程A能及时的获取到最新的值,是通过unsafe可以直接获取到主内存中的值,不是JMM的缓存一致性导致

假如我们模拟atomicInteger,对值+1

cas 有几个步骤:

1、通过对象,获取值t.getValue()

2、用A来比较当前对象内存地址的值,如果比较一致,则 把当前值+1,如果比较不一致,则cas失败

当前第二步比较与替换才是cas的操作,是原子性操作

如果只有第二部,就没有并发安全问题了,但是如果需要执行第一步跟第二步,高并发下多个线程,多个线程可能都会同时获取相同的值,但是最终只会有一个线程执行成功

执行不成功的只能自旋操作

如下:

import lombok.Data;

@Data
public class CasTestObjecy {
    private   int value = 0;
}
package com.fen.fou;

import org.springframework.objenesis.instantiator.util.UnsafeUtils;
import sun.misc.Unsafe;

public class CasTest {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException, InterruptedException {
        Unsafe u = UnsafeUtils.getUnsafe();
        CasTestObjecy t = new CasTestObjecy();
        long b1 = u.objectFieldOffset(CasTestObjecy.class.getDeclaredField("value"));

        for(int i=0 ; i< 5000 ; i++){
            new Thread(()->{
                for(int j=0 ;j<100;j++){
                    int count = 0;
                    while (!u.compareAndSwapInt(t, b1,t.getValue(),t.getValue()+1)){
                        System.out.println(++count);
                    };
                }
            }).start();
        }

        Thread.sleep(10000);
        System.out.println(Thread.currentThread().getName() + t.getValue());
    }
}

是不是有个疑问,被比较的值是直接用unsafe从内存中取,但是比较的值t.getValue(),其他线程修改了,当前线程还是拷贝副本中的值,t.getValue()并不能可见呀,所以会不会出现死循环现象?当然答案肯定不会?最多只会自旋,那到底是为什么呢?

再看个示例:


public class CasTest2 {
    static CasTestObjecy t = new CasTestObjecy();
    static   int i = 0;
    public static void main(String[] args) throws NoSuchFieldException{
        Unsafe u = UnsafeUtils.getUnsafe();

        long b1 = u.objectFieldOffset(CasTestObjecy.class.getDeclaredField("value"));
        new Thread(()->{
              int a;boolean flag;
        //     u.compareAndSwapInt(t, b1,6,1);
              while (true){
                  if(i == 1){
                      System.out.println(Thread.currentThread().getName() + "-------------------111----------------");
                  }

              }
        },"A").start();

        new Thread(()->{
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            i=1;
            u.compareAndSwapInt(t, b1,0,5);
         },"B").start();
    }
}

其中线程A会出现死循环,

package com.fen.fou;

import org.springframework.objenesis.instantiator.util.UnsafeUtils;
import sun.misc.Unsafe;

public class CasTest2 {
    static CasTestObjecy t = new CasTestObjecy();
    static   int i = 0;
    public static void main(String[] args) throws NoSuchFieldException{
        Unsafe u = UnsafeUtils.getUnsafe();

        long b1 = u.objectFieldOffset(CasTestObjecy.class.getDeclaredField("value"));
        new Thread(()->{

            while (true){
                if(i == 1){
                    System.out.println(Thread.currentThread().getName() + "-------------------c----------------");
                    break;
                }

            }
        },"C").start();
        new Thread(()->{
              int a;boolean flag;

              while (true){
                  u.compareAndSwapInt(t, b1,6,1);
                  if(i == 1){
                      System.out.println(Thread.currentThread().getName() + "-------------------a----------------");
                      break;
                  }

              }
        },"A").start();

        new Thread(()->{
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            i=1;
            u.compareAndSwapInt(t, b1,0,5);
         },"B").start();
    }
}

如果是这样,线程A不会出现死循环,程序正常退出,但是线程C会出现死循环

那这是为什么呢?是不是有点懵了?

那这个估计涉及到compareAndSwapInt的底层了,其执行成功后,会把当前的工作内存清除,然后重新从主内存去加载,

这样就cas就不会出现死循环的情况,则才能保证数据的一致性,大家也可以一起来讨论下这个问题,我还不太理解,这只是我的猜想、

cas带来的问题:

1、高并发下会出现cas自旋,对cpu压力大

2、出现ABA问题,要对操作对象加版本号

 

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值