Volatile可以用来修饰成员变量和静态成员变量,保证可见性、有序性
可见性:保证volatile修饰的变量每次读取都会从主从中进行读取而不是cpu缓存
有序性:对Volatile修饰变量进行写操作时,会在该操作后加上写屏障,进行读操作时会在读操作前加上读屏障。
写屏障(sfence)保证在该屏障之前的,对共享变量的改动,都同步到主存当中,并且会确保指令重排序时,禁止屏障前后的写操作跨越屏障。
读屏障(lfence)保证在该屏障之后,对共享变量的读取,加载的是主存中最新数据。并且会确保指令重排序时,禁止屏障前后的读操作跨越屏障
cas是compareAndSet的缩写,底层使用cpu指令,是一个比较并赋值的原子操作
结合 CAS 和 volatile 可以实现无锁并发,适用于线程数少、多核 CPU 的场景下。
CAS 体现的是无锁并发、无阻塞并发,请仔细体会这两句话的意思
因为没有使用 synchronized,所以线程不会陷入阻塞,这是效率提升的因素之一
但如果竞争激烈,可以想到重试必然频繁发生,反而效率会受影响
相比于
与之前提到的AtomicInteger不同,LongAdder 累加器采取了效率更高的方法应对高并发情况对数据的累加需求
LongAdder 关键字段
// 累加单元数组, 懒惰初始化
transient volatile Cell[] cells;
// 基础值, 如果没有竞争, 则用 cas 累加这个域
transient volatile long base;
// 在 cells 创建或扩容时, 置为 1, 表示加锁
transient volatile int cellsBusy;
当执行add(x)操作时大概执行流程如下:
判断当前是否发生过竞争
若有则判断当前线程是否有对应cell
若没有或者对base的cas累加失败或者对cell的cas累加失败
创建新的cell进行累加(具体创建与否需要根据机器cpu的核心数量)
将所有cell的值进行求和返回
不同CPU的寄存器中用到了不同的变量,一个用到的是X,一个用到的是Y,并且XY在同一个缓存行中,这就叫做缓存行伪共享
所谓缓存行就是cpu缓存的单位,每个缓存行大小为64个字节。当对缓存行中一个数据进行修改时,整个缓存行都将失效
LongAdder内部维护一个cells数组,包含多个cell(累加单元),每个cell绑定一或多个线程来分摊并发压力,然而,即使不同的cell对应不同的线程,但伪共享的出现会导致线程1对一个cell的修改导致另一个cell的值失效,需要重新从内存读取,大大影响其性能
此注解可以填充被修饰的对象,使一个缓存行最多只能存储一个该对象,解决伪共享问题
LongAdder通过使用多个cell分摊并发压力提高累加器性能,并且用@Contended注解修饰Cell对象,使其能够独占缓存行,解决了缓存行伪共享的问题。而AutomicInteger内部只用一个变量用来表示当前值,在高并发下多个线程争抢该变量性能损耗较大。