前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >AtomicInteger 核心源码解析

AtomicInteger 核心源码解析

原创
作者头像
JavaEdge
修改2020-05-06 18:02:15
3250
修改2020-05-06 18:02:15
举报
文章被收录于专栏:JavaEdgeJavaEdge

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

前言

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

继承关系

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

属性

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

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

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

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

构造方法

AtomicInteger 提供两个构造函数

  • 用给定值初始化 AtomicInteger
  • 无参构造,初始值为 0

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

API 源码

get - 获取当前值

set - 设为给定值

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

lazySet - 延迟设值

JDK 1.6 时引入.

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

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

getAndSet - 设新值,返旧值

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

代码语言:txt
复制
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:

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

基本的运算操作

自增

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

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

任意值的加减

CAS 操作

compareAndSet

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

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

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

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

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

weakCompareAndSet

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

总结

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 继承关系
  • 属性
  • 构造方法
  • API 源码
    • get - 获取当前值
      • set - 设为给定值
        • lazySet - 延迟设值
          • getAndSet - 设新值,返旧值
            • 基本的运算操作
              • 自增
            • 任意值的加减
              • CAS 操作
                • compareAndSet
                • weakCompareAndSet
            • 总结
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档