0
点赞
收藏
分享

微信扫一扫

JUC-CAS机制

天行五煞 2022-04-30 阅读 49
java

文章目录

CAS(比较并交换)

CAS 全称是 compare and swap,是一种用于在多线程环境下实现同步功能的机制。CAS 操作包含三个操作数 – 内存位置、预期数值和新值。CAS 的实现逻辑是将内存位置处的数值与预期数值想比较,若相等,则将内存位置处的值替换为新值。若不相等,则不做任何操作.
是CPU硬件级别提供的功能。java.util.concurrent包中使用该技术实现乐观锁,换句话说java.util.concurrent包是完全建立在CAS之上,AQS同步组件、Atomic原子类操作等都是基于CAS实现的。CAS在JUC包中所处的位置如图:

在这里插入图片描述

CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

在这里插入图片描述

java中具体的CAS操作是由类sun.misc.Unsafe来负责的。Unsafe类提供了硬件级别的原子操作(即native方法),Java使用native方法来间接访问操作系统底层(如系统硬件等),扩展Java程序的功能。native具体方法使用C++实现。sun.misc.Unsafe提供了三个CAS操作,从方法名即可看出,分别针对Object类型、int类型和long类型。

在这里插入图片描述

代码实例CAS:

具体方法:

public class CASDemo {
    public static void main(String[] args) {
		//设置初始值为1.
        AtomicInteger atomicInteger = new AtomicInteger(1);
        //CAS--比较并交换
        //如果期望值找到了就更新,负责不更新
        //CAS 是CPU的并发原语
        System.out.println(atomicInteger.compareAndSet(1, 2));
        System.out.println(atomicInteger.get());
        System.out.println(atomicInteger.compareAndSet(1, 2));
        System.out.println(atomicInteger.get());
    }
}

结果:由1变为2后,内存中的数据为2,再由1变2时变换失败。

true
2
false
2

上文说过CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B,但是仔细看Unsafe提供的CAS操作有4个操作数,这是因为Unsafe提供的CAS操作具体实现是使用C++来实现的,C++可以直接操作内存,效提升操作效率,直接用对象和字段偏移量来获取内存值V,第一个参数是要修改field所在的对象 ,第二个参数是对象中需要修改的field的偏移量(field偏移量这个参数所代表的意思是某个字段相对Java对象的“起始地址”的偏移量,Unsafe提供了一个方法objectFieldOffset(Field var1)来获取这个参数值,第三个参数就是旧的预期值A,第四个参数是要修改的新值B。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

CAS缺陷

①循环时间长导致CPU负载高

CAS操作一般和循环搭配使用(即我们所说的自旋CAS),失败了就会一直执行,直到修改成功才会退出循环。如果在某些并发量较大的情况下,变量的值始终被别的线程修改,自旋CAS长时间的不成功,则会给CPU带来非常大的开销。CAS 在并发量不是很高的情况下效率远远高于锁机制。还有一种策略就是限制CAS自旋的次数,在JUC中有些地方就使用了这种策略,例如阻塞队列中的SynchronousQueue。

在这里插入图片描述

②只能保证一个共享变量原子操作

CAS只能针对一个共享变量,如果是多个共享变量有三种方案:

  • 一是使用锁
  • 二是把多个变量合并为一个变量,比如有两个共享变量 i=2,j=a,合并一下 ij=2a,然后用CAS来操作ij;
  • 三是把多个变量放在一个对象里来使用,AtomicReference类进行CAS操作改变引用地址以达到修改变量的目的。
    在这里插入图片描述

③ABA问题

… …

CAS VS synchronized

对于资源竞争较少(线程冲突较轻)的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源;而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。

对于资源竞争严重(线程冲突严重)的情况,CAS自旋的概率会比较大,从而浪费更多的CPU资源,效率低于synchronized。

补充: synchronized 在 jdk1.6 之后,已经改进优化。synchronized 的底层实现主要依靠 Lock-Free 的队列,基本思路是自旋后阻塞,竞争切换后继续竞争锁,稍微牺牲了公平性,但获得了高吞吐量。在线程冲突较少的情况下,可以获得和 CAS 类似的性能;而线程冲突严重的情况下,性能远高于 CAS。

举报

相关推荐

0 条评论