原子类使用 CAS 替代锁,实现基本类似,我们本文以 AtomicInteger 为例来研究其究竟是如何实现无锁同步的.

前言

一个可以自动更新的int值。 AtomicInteger用于原子递增计数器之类的应用程序,并且不能用作Integer的替代品。 但是,此类确实继承了Number,以允许处理基于数字的类的工具和实用程序进行统一访问。

继承关系

AtomicInteger 核心源码解析_继承关系


  • 继承关系easy
    AtomicInteger 核心源码解析_继承关系_02
  • 由于继承了 Number,所以可以把 Number 代表的数值转换为基本数值类型
    AtomicInteger 核心源码解析_线程池_03

属性

  • 设置为使用 Unsafe.compareAndSwapInt 进行更新
    AtomicInteger 核心源码解析_线程池_04
    AtomicInteger 核心源码解析_无锁_05

AtomicInteger 核心源码解析_继承关系_06

注意到 unsafe 和 valueOffset 都是 static final 字段,而 value 有 volatile 修饰.

  • valueOffset 在静态代码块中完成初始化:
    AtomicInteger 核心源码解析_继承关系_07

AtomicInteger 的初衷就是在不使用锁的前提下,实现原子的读-改-写操作,这是通过 Unsafe 类提供的 CAS 操作实现的,CAS 操作有底层 CPU 直接支持。

构造方法

AtomicInteger 提供两个构造函数


  • 用给定值初始化 AtomicInteger
    AtomicInteger 核心源码解析_继承关系_08
  • 无参构造,初始值为 0
    AtomicInteger 核心源码解析_继承关系_09

注意,该类所有方法都被 final 修饰,子类无法重写!

API 源码

get - 获取当前值

AtomicInteger 核心源码解析_继承关系_10

set - 设为给定值

AtomicInteger 核心源码解析_线程池_11

由于 value 是 volatile 变量,通过内存屏障,set 对 value 的修改对其他线程是立即可见 的,无需添加 synchronized.

lazySet - 延迟设值

JDK 1.6 时引入.

大部分场景直接用 set 即可,但 set 内存屏障将禁止重排序,这会带来一定的性能消耗,因此非常关心性能,而lazySet不会立刻(但最终会)修改旧值,别的线程看到新值的时间会延迟一些

AtomicInteger 核心源码解析_线程池_12

lazySet 具有 write(assign)volatile 变量的内存效果,除了它允许对后续(但不是先前)的内存操作进行重排序,而这些内存操作本身不会对普通的非 volatile 写入施加强加约束。 在其他使用上下文中,为了进行垃圾回收,lazySet 可能在清空时适用,以后再也不会访问该引用。

getAndSet - 设新值,返旧值

因为 value 是 volatile 变量,所以对 value 的 read/write 具备 happens-before 关系,所以一个很容易想到的实现为:

AtomicInteger counter = new AtomicInteger();
int oldV = counter.get();
counter.set(10);

但该实现是错的,因为 counter.get() 与 counter.set(10) 之间可能插入其他线程的 set,所以 oldV 不能保证是 set(10) 执行时的 value 值,当然通过锁将 get 与 set(10) 变成原子操作可以满足需求,但我们使用 AtomicInteger 就是为了避免使用锁,所以也不能这么做.

于是有了getAndSet:

AtomicInteger 核心源码解析_继承关系_13

将 set 和 get 合并成一个原子操作,同时避免使用锁,依旧借助 unsafe 实现。

基本的运算操作

自增


  • 以原子方式将当前值增加一(i++)
    AtomicInteger 核心源码解析_线程池_14
  • 以原子方式将当前值增加一(++i)
    AtomicInteger 核心源码解析_无锁_15

自减


  • 以原子方式将当前值减一(i–)
    AtomicInteger 核心源码解析_无锁_16
  • 以原子方式将当前值减一(–i)AtomicInteger 核心源码解析_继承关系_17

都是基于 Unsafe.getAndAddInt 实现的,该方法实现 value 加操作,且返回旧值。

任意值的加减

AtomicInteger 核心源码解析_无锁_18

AtomicInteger 核心源码解析_继承关系_19

CAS 操作

compareAndSet

AtomicInteger 核心源码解析_继承关系_20

getAndSet 无脑更新 value ,并发场景下不会一直如此简单,有时要求 value 满足特定条件时才设置,这是非常典型的原子复合操作


  1. 检查某条件是否成立
  2. 根据条件成功、失败执行不同操作

在业务代码中,这种操作一般用锁实现,但 AtomicInteger 原生提供的 compareAndSet 无锁完美解决.

只有 value 的当前值等于 expect 时,才把 value 设置为 update,同时如果设置成功则返回 true,否则返回 false。

借助返回值可以检测方法的执行结果,因此可以在循环操作中不断执行 compareAndSet,直到成功,在线程池的源码中,很多方法都是这种套路。

weakCompareAndSet

  • 弱化版compareAndSet,可能会虚假地失败,并且不提供排序保证,因此,很少是compareAndSet的适当替代方法,JDK8源码中未曾使用过它,因为二者在 Java 源码层次是一模一样的.
    AtomicInteger 核心源码解析_继承关系_21

总结

AtomicIntger 的关键是 compareAndSet 方法,基于它可实现乐观的无锁算法.其妙用在 线程池中有着淋漓尽致地体现