NightPxy 个人技术博客

java-Atomic

Posted on By NightPxy

概述

volatile 只能保证原子性而不能保证可见性.所以对类似i++的本身不能保证原子性的操作是不能达到线程安全的.解决的常规思路是同步锁,但事实上为了简单自增使用同步锁是非常伤害性能
对于这类问题有一个更好的方案是,比如让CPU底层保证取值赋值的原子性.但底层只能保证取值赋值的原子性,不能处理if(i=A) i=B这种情况,所以统一提交两个值给底层如果值现在是A就更新B,如果不是就不更新,并让底层保证这个比较和更新是原子性的.而为了应对线程冲突,采用一个小循环去不断的提交CPU尝试,这样最终安全的将我们期望的值更新进去
这就是CAS(Compare And Swap),在这个思想指导下,JDK封装了一系列的基于CAS的线程安全帮助类,统称Atomic,其中包括AtomicInteger,AtomicLong,AtomicBoolean等等

CAS的优势在于使用的自旋锁而不是同步锁.自旋锁的优势在于它本质上不是一个锁,所以不会产生线程切换和阻塞.自旋锁的劣势在于冲突竞争以自旋尝试完成.自旋是一种空耗CPU的活锁表现,所以CAS适用于竞争不激烈的情况

AtomicInteger

ABA

Atomic基于CAS,在线程安全层面是没问题的,但在某些业务场景下可能会产生问题
这个问题是CAS是基于当前值比较完成,它不能体现出值的变化.这个问题的简单描述就是ABA ABA是指 如果在线程一中值由A->B->A,那么线程二以纯粹的CAS是不能感知到这种变化,这在某些业务要求顺向且不允许重复处理的场景中是有问题的.比如对每个账户10块钱以下的补10块钱,但业务要求只补一次.这个时候CAS就很难处理了,因为如果账户又提款变成10块以下,就会再次触发(这个场景非常牵强,纯粹只是为了表示ABA问题,事实上类似这样的场景谁也不会用Atomic来处理)

所以Atomic中有一个非常特殊的 AtomicStampedReference.它的核心思路是同时比较两个
由单纯的比较值变成同时比较值和版本号.