Atomic 类

不积跬步,无以至千里。不积小流,无以成江海。

实现原理

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
2
3
4
5
6
AtomicStampedReference类compareAndSet方法源码:

public boolean compareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp)

这里的 CAS 有四个参数,后两个参数就是版本号的旧值和新值。

Atomic 实现类

AtomicInteger

AtomicLong

AtomicBoolean

AtomicReference

AtomicStampedReference

AtomicMarkableReference

AtomicIntegerFieldUpdater

AtomicLongFieldUpdater

AtomicReferenceFieldUpdater

AtomicIntegerArray

AtomicLongArray

Atomic-ReferenceArray

Concurrent 包提供了 AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray 三个数组元素的原子操作。注意,这里并不是说对整个数组的操作是原子的,而是针对数组中一个元素的原子操作而言。