AtomicInteger类提供了原子性的访问和更新,而它的原子性实现原理则是基于CAS(compare and swap)技术。
CAS基本流程(read-update-write)
cas说白了就是获取某个数值A(read),然后对A进行一些逻辑运算为值为B(update),然后利用cas指令尝试对A进行更新为B(write)。如果之前的数值A没有改变,则意味着没有其他线程对它进行并发修改,更新成功。如果是数值已经被修改(不等于 A),要么选择重试,要么返回失败或成功的结果。
翻开AtomicInteger源码,可以看到它利用Unsafe 提供的底层能力进行地底层操作的,以volatile 关键字来修饰value以保证可见性。Unsafe算出对象字段value的内存地址偏移,然后完成更新操作。value使用volatile修饰可见,进而实现了原子访问更新。
写代码验证(多线程环境下,准备工作只执行一次)
UML图
代码解析
从输出结果可以看出,使用UnsafePerson.doSomething()准备工作高执行了4次
从输出结果可以看出,使用SafePersonWithCAS.doSomething(),准备工作只执行了一次
使用CAS的副作用
cpu资源消耗和ABA问题
- cpu资源消耗。cas更适合用于竞争短暂的场景,大多数场景下重试只会发生一次就成功了。但难免会有意外的情况,此时可以考虑限制自旋的次数,以此来减少不必要的cpu消耗。
- ABA问题。CAS是在更新的时候比较前值,可能在这期间发生了A-B-A的话。因此紧急比较数值是否相等(B之前的A=B之后的A)的话,可能造成不合理的修改操作。针对这种情况,Java提供了AtomicStampedReference工具类,建立类似版本号的方式来保证CAS正确性
总结
1.在代码中调用Unsafe类来实现锁操作并不是一个好的选择。例如Java 9 中移除了 Unsafe.moniterEnter()/moniterExit()方法,那么这就意味着使用Unsafe.moniterEnter()/monitorExit()方法的项目无法平滑升级JDK9,将增加额外工作量。