Java CAS原理分析

概念

  • CAS:Compare and Swap,实现并发算法时常用到的一种技术。CAS操作包含三个操作数——内存位置、预期原值及新值。执行CAS操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作。CAS是一条CPU的原子指令(cmpxchg指令),不会造成所谓的数据不一致问题,Unsafe提供的CAS方法(如compareAndSwapXXX)底层实现即为CPU指令cmpxchg

为什么要使用CAS?

多线程访问同一共享资源时,会出现三个问题:可见性、有序性、原子性

1.同步锁机制
  • 通过synchronized悲观锁获取对象的监视器,从而实现多个线程的互斥访问,也就是说,一旦有并发问题,实际上之后一个线程在进行操作,对于高并发并不具备优势
  • 同步锁机制虽然能保证happen before原则,线程1早于线程2获取到 锁,那么线程1一定早于线程2完成操作,但是对于大多数并发场景并不需要 这个原则,只需要保证结果的一致性即可,CAS操作保证最小力度上具备happen before原则,可以大大提升性能
2.Volatile轻量同步机制
  • 我们之前讲过Volatile关键字能够在并发条件下
  1. 强制将修改后的值刷新到主内存中来保持内存的可见性
  2. 通过 CPU内存屏障禁止编译器指令性重排来保证并发操作的有序性
  • 但是如何保证原子操作?
    就Volatile的内存可见性实现而言,很显然Volatile不能保证操作具有原子性,怎么解决原子性问题?CAS实现给了我们答案。

原理

  • CAS的实现是乐观锁的一种,他认为认为线程的并发访问不会产生 冲突,所以操作过程并不会枷锁,在操作结束需要更新共享内存资源时需要判断下其他线程时候修改了这个资源,如果没被修改,就将自己线程的值刷新到主内存中,如果在要更新的时候发现主内存的数据被其他线程更新,就处于重试状态,直到没有冲突为止
  1. CAS包含三个值当前内存值(V)、预期原来的值(A)以及期待更新的值(B)。
  2. 如果内存位置V的值与预期原值A相匹配,那么处理器会自动将该位置值更新为新值B,返回true。否则处理器不做任何操作,返回false。
  3. 比如当前线程比较成功后,准备更新共享变量值的时候,这个共享变量值被其他线程更改了,那么CAS函数必须返回false。
  • Java在实现这个操作时使用的Unsafe类来实现,提供了三个接口,入参obj用来获取初始对象的内存地址,valueOffset为内存地址的偏移值
  public final native boolean compareAndSwapObject
       (Object obj, long valueOffset, Object expect, Object update);

    public final native boolean compareAndSwapInt
       (Object obj, long valueOffset, int expect, int update);

    public final native boolean compareAndSwapLong
      (Object obj, long valueOffset, long expect, long update)

应用

JUC包下的AtomicInteger类

  • AtomicInteger的 自增调用Unsafe类的CAS操作
    /**
     * Atomically increments by one the current value.
     *
     * @return the previous value
     */
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }
	// unsafe类下的 getAndAddInt实现
	public final int getAndAddInt(Object var1, long var2, int var4) {
	    int var5;
	    do {
	        var5 = this.getIntVolatile(var1, var2);
	    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
	
	    return var5;
	}
  • 从实现上来看getAndAddInt有自旋操作,如果单次的CAS操作没有 成功,会继续进行CAS操作,CAS操作到了底层是靠CPU指令来实现的

CAS缺点

1. ABA问题
  • CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了
  • 解决ABA问题思路:
  1. 每次变量更新时进行版本号追加工作,进行版本号和内容组合比较
  2. 检查当前比较的值得引用是是否相等

2.需要CAS操作的地方基本需要自旋,自旋时间 过长会带来CPU大的开销

3.CAS操作的粒度只是针对一个共享变量,针对多个共享变量的原子操作而言,应该需要用锁

引申

  1. Java锁机制
    https://blog.csdn.net/weixin_41950473/article/details/92080488
  2. Java中的Volatile关键字
    https://blog.csdn.net/weixin_41950473/article/details/91966135
  3. Java Synchronized锁
    https://blog.csdn.net/weixin_41950473/article/details/90049998
  4. 什么是原子操作
    “原子操作(atomic operation)是不需要synchronized”,原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切 换到另一个线程)。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java CAS(Compare And Swap,比较并交换)是一种常用于多线程编程的原子操作。其主要作用是在多线程环境下保证变量的原子性和一致性,解决多线程竞争条件下的并发问题。 Java CAS原理是通过比较内存中的值与期望值是否相等来确定是否进行交换,其核心思想是利用硬件的原子性操作来实现并发的同步,而不需要使用锁(synchronized)等机制。 具体来说,CAS操作包含三个参数:内存地址、旧的预期值和新的值。 1. 首先,CAS会将当前内存地址中的值与旧的预期值进行比较,如果相等,则说明内存中的值未被其他线程修改。 2. 然后,CAS会使用新的值来更新内存地址中的值,完成交换操作。 3. 最后,CAS会返回旧的预期值,可以通过返回值进行判断操作是否成功。 需要注意的是,CAS是一种乐观的并发控制方式,它不会阻塞线程,而是通过不断重试的方式来保证操作的原子性。如果CAS操作失败,那么线程会重新读取内存中的值,并重新尝试进行CAS操作,直到成功为止。 然而,CAS也存在一些问题。首先,CAS需要频繁地读取和写入内存,这对内存带宽的要求较高;其次,由于CAS操作是无锁的,因此存在ABA问题,即在操作过程中,如果其他线程修改了预期值两次并恢复为原来的值,CAS操作无法察觉。 总之,Java CAS作为一种基于硬件支持的原子操作,可以在多线程环境下实现高效的同步控制,然而它也需要开发人员自行处理ABA问题以及确保程序的正确性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值