原子操作CAS小东西

什么是原子操作?

假如有俩个操作a和b,如果从执行a的线程来看,当另一个线程执行b的时候,要么将
b全部执行完,要不完全不执行b,那么a和b对于彼此来说就是原子的。

如何实现原子操作

实现原子操作的话,我们可以通过使用锁,锁机制来满足基本需求是没问题了,但是我们有时候需要更有效,更灵活的机制。我们用的synchronized关键字是基于阻塞的锁机制,就是当一个线程拥有锁的时候,访问同一资源的其他线程需要等待,直到锁资源被释放。
这样会出现一些问题:如果获得锁的线程一直不释放锁资源,或者有大量线程竞争资源,那cpu就会花费大量时间和资源来处理,可能还会出现死锁,对于这种比较粗糙,粒度比较大的机制,明显不太适合我们。

实现原子操作可以使用当前处理器基本都支持CAS()的指令。每一个CAS操作过程都包含三个运算符:一个内存地址V,一个期望值A和一个新值B,操作的时候如果这个内存地址上存放的值等于这个期望的值,就把地址上的值赋给新值B,否则不做任何操作。但是要返回原值是多少。循环CAS就是一个循环不断的做CAS操作(自旋),直到成功为止。

在这里插入图片描述

CAS如何实现线程安全:语言层面不做处理,利用cpu的多处理能力,实现硬件层面的阻塞,再加上volatile变量的特性来实现。

CAS实现原子操作的三大问题

  1. ABA问题
    在CAS操作值得时候,检查值有没有变化,如果没有发生变化则更新,但是如果这个值原来是A,然后变成了B,然后又变成了A,那么使用CAS进行检查时会发现这个值没有发生变化,但实际上是变化了,这就很恶心了。
    这就好像:你接了杯生命源泉放桌子上,然后被老板叫去,然后有个人过来喝了半杯,他又给你接满,你回来得时候水看着没变化,但其实是变化过了。(就问你恶心吗???)

解决办法:使用版本号,当你接好水后,版本号为1,那个人过来喝了半杯版本告是2,又给你接满就是3。 1A------2B------3A,有这么一个变量来储存这个版本号。

  • 开销大
    CAS操作当中,当那个内存地址值和期望值比较得时候,如果不相等,那么它会一直自旋,如果长时间不成功,会给cpu带来非常大得开销。
  • 只能保证一个共享变量的原子操作
    当对一个共享变量执行操作得时候,我们可以通过循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性了。这个时候就可以用锁。
    还有一个办法,就是把多个共享变量合并成一个共享变量来操作。

JDK中相关原子操作类的使用

在这里插入图片描述
我们就说一些平时用到概率大的类

更新基本类型

  • AtomicInteger
    int addAndGet(int delta) :输入的值和实例中的值(初始化的值)以原子方式相加返回结果
    boolean compareAndSet(int expect,int update):输入的值与预期值相等,则以原子方式将该值设置成输入值
    int getAndIncrement():以原子方式给当前值加1,但是返回的是自增前的值
    int getAndSet(int newValue):以原子方式设置为newValue的值,并返回旧值

  • AtomicIntegerArray
    int addAndGet(int i,int delta):以原子方式将输入值与数组中索引i的元素相加
    boolean compareAndSet(int i,int expect,int update):如果当前值等于预期值,则以原子方式将数组位置i的元素设置成update值

注意:数组value通过构造方法传递进去,然后AtomicIntegerArray会把前数组复制一份,所以当它对内部数组元素进行修改并不会影响传入的数组

更新引用类型

  • AtomicReference
    原子更新引用类型
  • AtomicStampedReference
    AtomicStampedReference 是使用 pair 的 int stamp 作为计数器使用,利用版本戳的形式记录了每次改变以后的版本号,这样的话就不会存在 ABA 问题了
  • AtomicMarkableReference
    原子更新带有标记位的引用类型。可以原子更新一个布尔类型的标记位和引 用类型。构造方法是 AtomicMarkableReference(V initialRef,booleaninitialMark)

AtomicStampedReference和AtomicMarkableReference他俩就像好基友,都可以解决ABA问题,但是侧重点不同,AtomicStampedReference侧重更新的次数,而AtomicMarkableReference侧重是否被人动过。

更新字段类型

  • AtomicIntegerFieldUpdater
    原子更新整型的字段的更新器
  • AtomicLongFieldUpdater
    原子更新长整型字段的更新器
  • AtomicReferenceFieldUpdater
    原子更新引用类型里的字段

原子更新字段类需要俩步:1.因为更新字段类都是抽象类,每次使用的时候必须使用静态方法newUpdater()创建一个更新器,然后去设置想要更新的类和属性。2.更新类的属性必须使用public volatile修饰符。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值