Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why AtomicReference CAS return false with value 128?

I was using AtomicReference to implement AtomicInteger. However while in testing I notice even in single threaded environment the CAS operation got stuck once its value hit 128.. Am I doing something wrong or there is a caveat in AtomicReference (may be related to CPU)? Here 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();
  }
}
like image 310
Alan Avatar asked Jun 13 '14 16:06

Alan


People also ask

What is use of AtomicReference in Java?

AtomicReference class provides operations on underlying object reference that can be read and written atomically, and also contains advanced atomic operations. AtomicReference supports atomic operations on underlying object reference variable.

When should I use AtomicReference?

An atomic reference is ideal to use when you need to share and change the state of an immutable object between multiple threads. That is a super dense statement, so I will break it down a bit. First, an immutable object is an object that is effectively not changed after construction.


1 Answers

The reason for this is that when you box an int into an Integer, you may or may not create a new Integer instance. If you do, then the new instance may not have reference equality with other Integer instances, even if they share the same value. AtomicReference.compareAndSet() uses reference equality (identity) for comparisons.

The key is in how the compiler handles automatic boxing of int values: it emits calls to Integer.valueOf(). As an optimization, Integer.valueOf() has a cache of boxed integers, and by default that cache includes values up to 128. If you box an integer n twice, you will get the same Integer reference back each time if the value was small enough to be in the cache; otherwise, you will get two separate instances.

Currently, you unbox the old value, compute the new value, and when you call compareAndSet() you box the old value again. Once you hit 128, you stop getting cached values, so the second boxed copy is no longer the same one that's in the AtomicReference.

like image 91
Mike Strobel Avatar answered Sep 27 '22 17:09

Mike Strobel