前言
线程的ABA的问题有可能导致在搞并发的时候会产生不同的线程多次的修改,导致我们得到的结果和想要的不一致。
正文
ABA问题的产生
想要知道什么是ABA问题,首先我们来通俗的看一下这个例子:商店为了生意推出了一个特别活动,凡是在五一期间的老用户凡是卡里余额小于30的,赠送10元,但是这种活动没人只可享受一次。然后火锅店的后台程序员小王开始工作了,很简单就用cas技术,先去用户卡里的余额,然后包装成AtomicInteger,写一个判断,开启10个线程,然后判断小于30的,一律加30,然后就很开心的交差了。可是过了一段时间,发现账面亏损的厉害,老板起先的预支是3000块,因为店里的会员总共也就100多个,就算每人都符合条件,最多也就3000啊,怎么预支了这么多。小王一下就懵逼了,赶紧debug,tail -f一下日志,这不看不知道,一看吓一跳,有个客户被充值了10次!
阐述:
假设有个线程A去判断账户里的钱此时是15,满足条件,直接+30,这时候卡里余额是45.但是此时不巧,正好在连锁店里,这个客人正在消费,又消费了30,此时卡里余额又为15,线程B去执行扫描账户的时候,发现它又小于30,又用过cas给它加了30,这样的话就相当于加了两次,这样循环往复肯定把老板的钱就坑没了!
本质:
ABA问题的根本在于cas在修改变量的时候,无法记录变量的状态,比如修改的次数,是否修改过这个变量。这样就很容易在一个线程将A修改成B时,另一个线程又会把B修改成A,造成casd多次执行的问题。
解决的方法:
使用AtomicStampReference 来进行解决我们这个ABA的问题。
AtomicStampReference在cas的基础上增加了一个标记stamp,使用这个标记可以用来觉察数据是否发生变化,给数据带上了一种实效性的检验。它有以下几个参数:
//参数代表的含义分别是 期望值,写入的新值,期望标记,新标记值 public boolean compareAndSet(V expected,V newReference,int expectedStamp,int newStamp); public V getRerference(); public int getStamp(); public void set(V newReference,int newStamp);