JUC(无锁并发)
CAS
CAS:compareAndSet(比较并设置)
底层是lock cmpxchg指令(X86架构),在单核CPU和多核CPU下都能保证比较-交换的原子性
在多核状态下,某个核执行到带lock的指令时,CPU会让总线锁住,当这个核把此指令执行完毕,在开启总线,这个过程中不会被线程的调度机制所打断,保证了多个线程对内存操作的准确性,是原子的
CAS保存的变量用volatile修饰,保证变量可见性
为什么无锁效率更高
无锁情况下,即使重试失败,线程始终告诉运行,没有停歇,而synchronized会让线程在没有获得锁的时候,发生上下文切换,进入阻塞
CAS特点
结合CAS和volatile可以实现无锁并发,适用于线程数少,多核CPU的场景下
- CAS是基于乐观锁的思想:最乐观的估计,不怕别的线程来修改共享变量,我重试就行
- synchronized是基于悲观锁的思想,最悲观的估计,得防着其他线程修改共享变量,一次只能一个人进入临界区
- CAS体现的是无锁并发、无阻塞并发
- 因为没有使用synchronized,所以线程不会陷入阻塞,这是效率提升的因素之一
- 但如果竞争激烈,可以想到重试必然频繁发生,效率会受到影响
原子整数
JUC并发包提供了:
- AtomicBoolean
- AtomicInteger
- AtomicLong
compareAndSet(expect,update):比较并赋值
加法
incrementAndGet():自增并获取值
getAndIncrement():获取并自增
getAndAdd(data):获取并增加
addAndGet(data):增加并获取
复杂运算
updateAndGet(函数式接口(传入初始值,返回设置值))
getAndUpdate(函数式接口(传入初始值,返回设置值))
原子引用类型
- AtomicReference
- AtomicMarkableReference
- AtomicStampedReference
atomicReference:通过比较对象地址判断共享对象是否改变
会有ABA问题
AtomicStampedReference:通过版本号判断共享对象是否改变(只有版本号和对象都没有改变更改)
解决ABA问题
AtomicMarkableReference:有时候我们并不关心引用变量更改了几次,只是单纯关心是否被更改过
如果你需要记录修改了多少次(防止任何形式的重叠),请使用 AtomicStampedReference,并配合自增的 int 版本号。
如果你只需要一个二元状态标记(比如:这封邮件是否已被读取?这个节点是否已被标记删除?),才使用 AtomicMarkableReference。
原子数组
- AtomicIntegerArray
- AtomicLongArray
- AtomicReferenceArray
字段更新器
- AtomicRefrerenceFieldUpdater //字段
- AtomicIntegerFieldUpdater
- AtomicLongFieldUpdate
创建:AtomicReferenceFieldUpdater.newUpdater(类名,属性类型,属性名)
注意:字段一定加volatile修饰
原子累加器
- LongAccumulator
- LongAdder
相比AtomicInteger、AtomicLong性能更好
原因:有竞争时,设置多个累加单元,Thread-0累加cell[0],而Thread-1累加cell[1]…最后将结果汇总,这样他们在累加时操作的不同cell变量,因此减少了CAS重试失败,从而提高性能
伪共享:一个缓存行(64字节)放入多条数据
@Contended注解(防止伪共享),通过加入padding,使数据放入不同缓存行,避免不相关数据之间的修改导致频繁地缓存失效
LongAdder 中 cell 的创建与使用流程:
- 先尝试对 base 做 CAS,成功直接返回
- 若失败,进入 cells 逻辑(longAccumulate)
- 若 cells 未初始化,尝试通过 CAS 获取锁并创建 cells 数组(初始长度为2)
- 若 cells 已存在:
- 根据线程 probe 定位 cell
- 若 cell 存在,CAS 更新
- 若不存在,尝试创建 cell(需加锁)
- 若 CAS 冲突:
- 若 cells 长度小于 CPU 核数,尝试扩容
- 否则通过 rehash 更换槽位,减少冲突
- 整个过程在自旋中完成,直到成功更新
Unsafe
提供了非常底层的,操作内存、线程的方法
更多推荐




所有评论(0)