Java – why does atomicreference CAS return false with a value of 128?
I use atomicreference to implement atomicinteger However, in testing, I noticed that even in a single threaded environment, CAS operations get stuck once the value reaches 128 What did I do wrong or have a warning in the atomicreference (possibly CPU related)? This is my code:
public class MyAtomInt { private final AtomicReference<Integer> ref; public MyAtomInt(int init) { ref = new AtomicReference<Integer>(init); } public MyAtomInt() { this(0); } public void inc() { while (true) { int oldVal = ref.get(); int nextVal = oldVal + 1; boolean success = ref.compareAndSet(oldVal,nextVal); // false once oldVal = 128 if (success) { return; } } } public int get() { return ref.get(); } static class Task implements Runnable { private final MyAtomInt myAtomInt; private final int incCount; public Task(MyAtomInt myAtomInt,int cnt) { this.myAtomInt = myAtomInt; this.incCount = cnt; } @Override public void run() { for (int i = 0; i < incCount; ++i) { myAtomInt.inc(); } } } public static void main(String[] args) throws Exception { MyAtomInt myAtomInt = new MyAtomInt(); ExecutorService exec = Executors.newSingleThreadExecutor(); exec.submit(new Task(new MyAtomInt(),150)).get(); System.out.println(myAtomInt.get()); exec.shutdown(); } }
Solution
The reason for this is that when you load int into integer, you may or may not create a new integer instance If you do this, the new instance may not be equal to other integer instances, even if they share the same value AtomicReference. Compareandset() uses reference equality (identity) for comparison
The key is how the compiler handles the auto boxing of int values: it issues a call to integer Call to valueof() As an optimization, integer Valueof() has a cache of boxed integers. By default, the cache contains up to 128 values If you wrap the integer n twice, if the value is very small, you will get the same integer reference each time, which is enough to enter the cache; Otherwise, you will get two separate instances
At present, you cancel loading the old value, calculate the new value, and load the old value again when you call compareandset() Once 128 is reached, fetching the cache value is stopped, so the second boxed copy is no longer the same as the copy in the atomicreference