美文网首页程序员JDK 源码
AtomicInteger 核心源码解析

AtomicInteger 核心源码解析

作者: JavaEdge | 来源:发表于2020-05-06 01:23 被阅读0次

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

    前言

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

    继承关系

    image
    • 继承关系easy


      image
    • 由于继承了 Number,所以可以把 Number 代表的数值转换为基本数值类型


      image

    属性

    • 设置为使用 Unsafe.compareAndSwapInt 进行更新


      image
      image
    image

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

    • valueOffset 在静态代码块中完成初始化:


      image

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

    构造方法

    AtomicInteger 提供两个构造函数

    • 用给定值初始化 AtomicInteger


      image
    • 无参构造,初始值为 0


      image

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

    API 源码

    get - 获取当前值

    image

    set - 设为给定值

    image

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

    lazySet - 延迟设值

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


    image

    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:


    image

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

    基本的运算操作

    自增

    • 以原子方式将当前值增加一(i++)


      image
    • 以原子方式将当前值增加一(++i)


      image

    自减

    • 以原子方式将当前值减一(i--)


      image
    • 以原子方式将当前值减一(--i) image

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

    任意值的加减

    image
    image

    CAS 操作

    compareAndSet

    image

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

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

    在业务代码中,这种操作一般用锁实现,但 AtomicInteger 原生提供的 compareAndSet 无锁完美解决.
    只有 value 的当前值等于 expect 时,才把 value 设置为 update,同时如果设置成功则返回 true,否则返回 false。
    借助返回值可以检测方法的执行结果,因此可以在循环操作中不断执行 compareAndSet,直到成功,在线程池的源码中,很多方法都是这种套路。

    weakCompareAndSet

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


      image

    总结

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

    相关文章

      网友评论

        本文标题:AtomicInteger 核心源码解析

        本文链接:https://www.haomeiwen.com/subject/ltwbghtx.html