AtomicInteger 核心源码解析

原子类使用 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 方法,基于它可实现乐观的无锁算法.其妙用在 线程池中有着淋漓尽致地体现

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352