知识卡片
ABA要看历史
内容
ABA 问题的危险不在最终值相同,而在中间状态被抹掉:变量从 A 变到 B 再变回 A,普通 CAS 只比较当前值与预期旧值,会误以为期间无变化。书中链表栈例子:线程 A 准备弹出栈顶 E2,被线程 B 抢先弹出后又压回新 E2,A 恢复执行时”成功”弹出,但 B 压入的 E3 已被静悄悄丢弃。需用版本号(AtomicStampedReference)把历史变化纳入比较。
参考来源
- 位置:《Java高并发核心编程.卷2,多线程、锁、JMM、JUC、高并发设计模式》第3章《CAS原理与JUC原子类》3.4.1节《了解ABA问题》、3.4.2节《ABA问题解决方案》(源文件:_epub-src/OEBPS/Text/chapter111.xhtml、chapter112.xhtml)
- 结论依据:原文用链表栈弹出/压入的具体步骤说明线程A的CAS(E2,E1)在栈顶被线程B操作后又变回同一个E2时仍会“成功”,但此时E3已被平白丢弃;随后给出版本号方案,指出“操作序列A==>B==>A就会变成A1==>B2==>A3”从而让CAS失败,因此可以推出本卡结论。
- 原始内容:线程A执行完成后,线程B之前压入的E3元素处于游离状态,不再存在于堆栈中,平白无故被丢掉了,这就是ABA问题引发的不正常状态……乐观锁每次在执行数据的修改操作时都会带上一个版本号,版本号和数据的版本号一致就可以执行修改操作并对版本号执行加1操作,否则执行失败。