什么是ABA问题 ABA就是有两个线程共享一个变量value,A线程读取到的时候这个值为1,准备修改的时候值还是1,但是可能这过程中B线程修改了变量1->2->1,仅仅用CAS无法得知这种修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import java.util.concurrent.atomic.AtomicInteger;public class ABAProblemExample { private static AtomicInteger atomicValue = new AtomicInteger (1 ); public static void main (String[] args) throws InterruptedException { Thread threadA = new Thread (() -> { int expectedValue = atomicValue.get(); System.out.println("Thread-A reads value: " + expectedValue); try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } boolean isSuccess = atomicValue.compareAndSet(expectedValue, 2 ); System.out.println("Thread-A CAS result: " + isSuccess + ", new value: " + atomicValue.get()); }); Thread threadB = new Thread (() -> { try { Thread.sleep(500 ); } catch (InterruptedException e) { e.printStackTrace(); } atomicValue.compareAndSet(1 , 3 ); System.out.println("Thread-B changes value to: " + atomicValue.get()); atomicValue.compareAndSet(3 , 1 ); System.out.println("Thread-B changes value back to: " + atomicValue.get()); }); threadA.start(); threadB.start(); threadA.join(); threadB.join(); } }
上面这个程序会输出
1 2 3 4 Thread -A reads value: 1 Thread -B changes value to : 3 Thread -B changes value back to : 1 Thread -A CAS result: true , new value: 2
使用AtomicStampedReference解决 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 import java.util.concurrent.atomic.AtomicStampedReference;public class ABAProblemSolution { private static AtomicStampedReference<Integer> atomicStampedRef = new AtomicStampedReference <>(1 , 0 ); public static void main (String[] args) throws InterruptedException { Thread threadA = new Thread (() -> { int [] stampHolder = new int [1 ]; Integer expectedValue = atomicStampedRef.get(stampHolder); int currentStamp = stampHolder[0 ]; System.out.println("Thread-A reads value: " + expectedValue + " with stamp: " + currentStamp); try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } boolean isSuccess = atomicStampedRef.compareAndSet(expectedValue, 2 , currentStamp, currentStamp + 1 ); System.out.println("Thread-A CAS result: " + isSuccess + ", new value: " + atomicStampedRef.getReference() + " with stamp: " + atomicStampedRef.getStamp()); }); Thread threadB = new Thread (() -> { try { Thread.sleep(500 ); } catch (InterruptedException e) { e.printStackTrace(); } int currentStamp = atomicStampedRef.getStamp(); atomicStampedRef.compareAndSet(1 , 3 , currentStamp, currentStamp + 1 ); System.out.println("Thread-B changes value to: " + atomicStampedRef.getReference() + " with stamp: " + atomicStampedRef.getStamp()); atomicStampedRef.compareAndSet(3 , 1 , atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1 ); System.out.println("Thread-B changes value back to: " + atomicStampedRef.getReference() + " with stamp: " + atomicStampedRef.getStamp()); }); threadA.start(); threadB.start(); threadA.join(); threadB.join(); } }
上面的程序会输出
1 2 3 4 Thread -A reads value: 1 with stamp: 0 Thread -B changes value to : 3 with stamp: 1 Thread -B changes value back to : 1 with stamp: 2 Thread -A CAS result: false , new value: 1 with stamp: 2
原因是AtomicStampedReference的CAS操作需要比较版本号,如果期待的版本号不等,则CAS失败
1 2 3 4 5 6 7 8 9 10 11 12 public boolean compareAndSet (V expectedReference, V newReference, int expectedStamp, int newStamp) { Pair<V> current = pair; return expectedReference == current.reference && expectedStamp == current.stamp && ((newReference == current.reference && newStamp == current.stamp) || casPair(current, Pair.of(newReference, newStamp))); }