不积跬步,无以至千里。不积小流,无以成江海。
实现原理
Atomic 类是基于 CAS 的乐观锁实现。
以 AtomicInteger 为例:AtomicInteger 封装过的 compareAndSet 有两个参数。第一个参数 expect 是指变量的旧值;第二个参数 update 是指变量的新值。当 expect 等于变量当前的值时,说明在修改的期间,没有其他线程对此变量进行过修改,所以可以成功写入,变量被更新为 update,返回 true;否则返回 false。
自旋与阻塞
当一个线程拿不到锁的时候,有以下两种基本的等待策略。
策略1:放弃 CPU,进入阻塞状态,等待后续被唤醒,再重新被操作系统调度。
策略2:不放弃 CPU,空转,不断重试,也就是所谓的“自旋”。
如果是单核的 CPU,只能用策略1。因为如果不放弃 CPU,那么其他线程无法运行,也就无法释放锁。但对于多 CPU 或者多核,策略2就很有用了,因为没有线程切换的开销。AtomicInteger 的实现就用的是“自旋”策略,如果拿不到锁,就会一直重试。有一点要说明:这两种策略并不是互斥的,可以结合使用。如果拿不到锁,先自旋几圈;如果自旋还拿不到锁,再阻塞,synchronized 关键字就是这样的实现策略。
ABA 问题
CAS 都是基于“值”来做比较的。但如果另外一个线程把变量的值从 A 改为 B,再从B改回到A,那么尽管修改过两次,可是在当前线程做 CAS 操作的时候,却会因为值没变而认为数据没有被其他线程修改过,这就是所谓的 ABA 问题。
要解决 ABA 问题,不仅要比较“值”,还要比较“版本号”,这正是 AtomicStamped-Reference 做的事情。
1 |
|
这里的 CAS 有四个参数,后两个参数就是版本号的旧值和新值。
Atomic 实现类
AtomicInteger
AtomicLong
AtomicBoolean
AtomicReference
AtomicStampedReference
AtomicMarkableReference
AtomicIntegerFieldUpdater
AtomicLongFieldUpdater
AtomicReferenceFieldUpdater
AtomicIntegerArray
AtomicLongArray
Atomic-ReferenceArray
Concurrent 包提供了 AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray 三个数组元素的原子操作。注意,这里并不是说对整个数组的操作是原子的,而是针对数组中一个元素的原子操作而言。