cas原理和volatile

volatile为什么不能保证原子性?

1.首先volatile可以保证变量每次读的都是最新值。但是在并发操作中,向i++,这种操作不是原子性,因为这里涉及到了多个操作,1.线程A 获取i的值100,这个时候获取的是最新值,此时由于线程阻塞等原因,线程B获取的最新值100,然后将i 自增到101,更新到内存。
此时线程A又来进行一次自增操作,又将101更新到内存。两个自增结果都是101,跟我们预期不一致,所以并不是原子操作。但是,每次获取值都是最新的,volatile保证了可见了,保证每次值的更新都更新到了主存,每次获取到的都是最新值

cas 原理?

cas原子性原理

将预期值和内存的值进行比较,如果两者相等就进行更新操作,更新操作是通过unsafe调用的操作系统底层的方法,大概是cpu会将缓存的值进行加一个总线锁,保证数据操作的原子性。

cas如何保证可见性?

我们通过cas处理的变量,可以设置为volatile,cpu底层会采用内存屏障保证数据可见性,cpu中有个高速缓存,每次更新值,会直接更新到主内存,刷新高速缓存。读取值的时候,会直接从主存读取数据

cas缺点

缺点有三。
1.cas是一种类似乐观锁的时间,如果资源的竞争比较激烈。那么每次都到内存做cas判断,会想好资源。这时候可能还没有悲观锁的好。
2.cas可能存在ABA的问题,第一:线程 1 和线程2都读取值 A,但是线程1执行很快,将值更新为B,一番操作后,线程2又更新为A,这时候线程1进行cas操作,发现内存中是A,然后继续操作。所以也是执行成功,但是整个过程有问题,因为数据做了变更,但是线程1却不知道。
解决办法: java中提供了一个AtomicStampedReference来处理ABA问题,相当于在对象中额外增加一个标记来标记对象是否有过变更。
3.cas只能保证单个标量的原子性,却不能同时保证多个变量的原子性,可以将把多个共享变量合并成一个共享变量来操作。比如有两个共享变量i=2,j=a,合并一下ij=2a,然后用CAS来操作ij

在java的并发包中,通常用cas配合变量的volatile,保证数据操作的原子性可见性。

CAS包含哪些参数:

unsafe.compareAndSwapObject(this, headOffset, null, update)

三个运算符:

  • 个是this,也就是unsafe这个对象

  • 一个内存地址V

  • 一个期望的值A

  • 一个新值B

  • 基本思路:

如果地址V上的值和期望的值A相等,就给地址V赋值新值B,如果不是,不做任何操作

循环CAS:

在一个(死)循环中里不断进行CAS操作,直到成功为止(自旋操作即死循环)
每次操作都取出最新值跟内存中的比较,cas比较失败,就从新拿最新值进行比较。比如在可重入锁的节点加入队列时候,用到自旋的cas。缺点是竞争比较高时候,无效的自旋操作浪费性能。

    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

double check 单例模式中,变量为什么定义成volatile?

比如定义一个person: Person p = new Person().

这里大概涉及到三个操作。
第一:new person 在内存开辟空间。
第二:调用person构造方法
第三:将new person的引用复制给变量p.
由于jvm的内部优化,执行过程可能会指令重排。 也就是说,将变量的赋值操作和对象的构造方法操作,可能会重排序,那么double check在并发的时候,可能拿到的一个空的对象,也就是没有属性值的对象。
所以可以使用volatile,禁止指令重排序,相当于是加了一个内存屏障,数据每次读都是从主存中读,写会更新到主存,刷新缓存。
但是volatile不能保证原子,类似变量的自增操作这种,不是一个原子操作。只能保证每次读的都是最新数据,不会说存在线程A更新了,线程B却读的老的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EmineWang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值