ABA问hiyi题:
在多线程环境下,一个线程需要修改共享变量的值,使用CAS操作时,当其他线程将该共享变量由A该为B,再将B改为A后,这个线程依然可以CAS操作成功,因为这个线程不能感知这个共享变量被修改过
解决方法:给共享变量增加一个版本号,在CAS操作时不仅比较值是否相等,还比较版本号是否相等,,,因为所有的线程使用共享变量时都遵循相同的规则:给版本号加1
代码示例:
@Slf4j(topic = "c.TestABA")
public class TestABA {
static AtomicStampedReference<String> ref=new AtomicStampedReference<>("A",0);
public static void main(String[] args) throws InterruptedException {
log.debug("main start....");
//获取值 A,获取版本号0
//这个共享变量被其他线程修改过?
String prev=ref.getReference();
int stamp=ref.getStamp();
log.debug("{}",stamp);
other();
TimeUnit.SECONDS.sleep(3);
//尝试改为C
log.debug("change A->C {}",ref.compareAndSet(prev,"C",stamp,stamp+1));
}
private static void other() throws InterruptedException {
new Thread(()->{
int stamp=ref.getStamp();
log.debug("changeA->B{}",ref.compareAndSet(ref.getReference(),"B",stamp,stamp+1));
log.debug("{}",stamp);
},"t1").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
int stamp=ref.getStamp();
log.debug("changeB->A{}",ref.compareAndSet(ref.getReference(),"A",stamp,stamp+1));
log.debug("{}",stamp);
},"t2").start();
}
}
运行结果:
08:50:26.076 [main] DEBUG c.TestABA - main start…
08:50:26.078 [main] DEBUG c.TestABA - 0
08:50:26.119 [t1] DEBUG c.TestABA - changeA->Btrue
08:50:26.119 [t1] DEBUG c.TestABA - 0
08:50:27.125 [t2] DEBUG c.TestABA - changeB->Atrue
08:50:27.125 [t2] DEBUG c.TestABA - 1
08:50:30.128 [main] DEBUG c.TestABA - change A->C false
结果分析:
一开始主线程先启动,去获取共享变量的值和版本号后,去调用other方法这个方法中启动两个线程去修改A与B的值,修改的规则一致:获取当前值和版本号,用CAS比较并设置,这个other()执行完成后,主线程再进行CAS操作时发现版本号被修改,此次的CAS将失败: change A->C false