JUC03-volatile、CAS及并发原子类

一、 Volatile

Volatile可以用来修饰成员变量和静态成员变量,保证可见性、有序性

可见性:保证volatile修饰的变量每次读取都会从主从中进行读取而不是cpu缓存
有序性:对Volatile修饰变量进行写操作时,会在该操作后加上写屏障,进行读操作时会在读操作前加上读屏障。

读写屏障

写屏障(sfence)保证在该屏障之前的,对共享变量的改动,都同步到主存当中,并且会确保指令重排序时,禁止屏障前后的写操作跨越屏障。

读屏障(lfence)保证在该屏障之后,对共享变量的读取,加载的是主存中最新数据。并且会确保指令重排序时,禁止屏障前后的读操作跨越屏障

重排序

在这里插入图片描述

二、CAS

cas是compareAndSet的缩写,底层使用cpu指令,是一个比较并赋值的原子操作

结合 CAS 和 volatile 可以实现无锁并发,适用于线程数少、多核 CPU 的场景下。

  • CAS 是基于乐观锁的思想:最乐观的估计,不怕别的线程来修改共享变量,就算改了也没关系,我吃亏点再重试呗。
  • synchronized 是基于悲观锁的思想:最悲观的估计,得防着其它线程来修改共享变量,我上了锁你们都别想改,我改完了解开锁,你们才有机会。

CAS 体现的是无锁并发、无阻塞并发,请仔细体会这两句话的意思
因为没有使用 synchronized,所以线程不会陷入阻塞,这是效率提升的因素之一
但如果竞争激烈,可以想到重试必然频繁发生,反而效率会受影响

相比于

三、 原子累加器LongAdder

与之前提到的AtomicInteger不同,LongAdder 累加器采取了效率更高的方法应对高并发情况对数据的累加需求

优化一:多个cell分摊并发压力

LongAdder 关键字段

// 累加单元数组, 懒惰初始化
transient volatile Cell[] cells;
// 基础值, 如果没有竞争, 则用 cas 累加这个域
transient volatile long base;
// 在 cells 创建或扩容时, 置为 1, 表示加锁
transient volatile int cellsBusy;

当执行add(x)操作时大概执行流程如下:

  1. 判断当前是否发生过竞争

    • 若没有则使用cas对base进行累加
  2. 若有则判断当前线程是否有对应cell

    • 有则使用cas对该cell进行累加
  3. 若没有或者对base的cas累加失败或者对cell的cas累加失败

  4. 创建新的cell进行累加(具体创建与否需要根据机器cpu的核心数量)

  5. 将所有cell的值进行求和返回

优化二:缓存行伪共享

不同CPU的寄存器中用到了不同的变量,一个用到的是X,一个用到的是Y,并且XY在同一个缓存行中,这就叫做缓存行伪共享

所谓缓存行就是cpu缓存的单位,每个缓存行大小为64个字节。当对缓存行中一个数据进行修改时,整个缓存行都将失效
在这里插入图片描述
LongAdder内部维护一个cells数组,包含多个cell(累加单元),每个cell绑定一或多个线程来分摊并发压力,然而,即使不同的cell对应不同的线程,但伪共享的出现会导致线程1对一个cell的修改导致另一个cell的值失效,需要重新从内存读取,大大影响其性能
在这里插入图片描述

@Contended注解填充缓存行解决伪共享

此注解可以填充被修饰的对象,使一个缓存行最多只能存储一个该对象,解决伪共享问题
在这里插入图片描述

总结

LongAdder通过使用多个cell分摊并发压力提高累加器性能,并且用@Contended注解修饰Cell对象,使其能够独占缓存行,解决了缓存行伪共享的问题。而AutomicInteger内部只用一个变量用来表示当前值,在高并发下多个线程争抢该变量性能损耗较大。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值