AtomicInteger底层实现原理是什么?如何在自己的产品代码中应用CAS操作?
在今天这一讲中,我来分析一下并发包内部的组成,一起来看看各种同步结构、线程池等,是基于什么原理来设计和实现的
今天我要问你的问题是,AtomicInteger底层实现原理是什么?如何在自己的产品代码中应用CAS操作?
典型回答
-
AtomicIntger是对int类型的一个封装,提供原子性的访问和更新操作,其原子性操作的实现是基于CAS(compare-and-swap)技术
-
所谓CAS,表征的是一些列操作的集合,获取当前数值,进行一些运算,利用CAS指令试图进行更新。如果当前数值未变,代表没有其他线程进行并发修改,则成功更新。否则,可能出现不同的选择,要么进行重试,要么就返回一个成功或者失败的结果
-
从AtomicInteger的内部属性可以看出,它依赖于Unsafe提供的一些底层能力,进行底层操作;以volatile的value字段,记录数值,以保证可见性
-
private satic fnal jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); private satic fnal long VALUE = U.objectFieldOfset(AtomicInteger.class, "value"); private volatile int value;
-
具体的原子操作细节,可以参考任意一个原子更新方法,比如下面的getAndIncrement
-
Unsafe会利用value字段的内存地址偏移,直接完成操作
-
public fnal int getAndIncrement() { return U.getAndAddInt(this, VALUE, 1); }
-
因为getAndIncrement需要返归数值,所以需要添加失败重试逻辑
-
public fnal int getAndAddInt(Object o, long ofset, int delta) { int v; do { v = getIntVolatile(o, ofset); } while (!weakCompareAndSetInt(o, ofset, v, v + delta)); return v; }
-
而类似compareAndSet这种返回boolean类型的函数,因为其返回值表现的就是成功与否,所以不需要重试
-
public fnal boolean compareAndSet(int expectedValue, int newValue)
-
CAS是Java并发中所谓lock-free机制的基础
考点分析
- 今天的问题有点偏向于 并发机制的底层了,虽然我们在开发中未必会涉及 的实现层面,但是理解其机制,掌握如何在 中运用该技术,还是十分有必要的,尤其是这也是个并发编程的面试热点
- 有的同学反馈面试官会问CAS更加底层是如何实现的,这依赖于CPU提供的特定指令,具体根据体系结构的不同还存在着明显区别。比如,x86 CPU提供cmpxchg指令;而在精简指令集的体系架构中,则通常是靠一对儿指令(如“load and reserve”和“store conditional”)实现的,在大多数处理器上CAS都是个非常轻量级的操作,这也是其优势所在
- 大部分情况下,掌握到这个程度也就够用了,我认为没有必要让每个Java工程师都去了解到指令级别,我们进行抽象、分工就是为了让不同层面的开发者在开发中,可以尽量屏蔽不相关的细节
- 如果我作为面试官,很有可能深入考察这些方向:
- 在什么场景下,可以采用CAS技术,调用Unsafe毕竟不是大多数场景的最好选择,有没有更加推荐的方式呢?毕竟我们掌握一个技术,cool不是目的,更不是为了应付面试,我们还是希望能在实际产品中有价值
- 对ReentrantLock、CyclicBarrier等并发结构底层的实现技术的理解