我使用AtomicReference来实现AtomicInteger.然而,在测试中,我注意到即使在单线程环境中,一旦其值达到128,CAS操作就会卡住.我做错了什么或在AtomicReference中有一个警告(可能与cpu有关)?这是我的代码:
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(); } }
解决方法
这样做的原因是当您将int装入Integer时,您可能会也可能不会创建新的Integer实例.如果这样做,则新实例可能与其他Integer实例没有引用相等,即使它们共享相同的值. AtomicReference.compareAndSet()使用引用相等(标识)进行比较.
关键在于编译器如何处理int值的自动装箱:它发出对Integer.valueOf()的调用.作为优化,Integer.valueOf()具有盒装整数的缓存,默认情况下,缓存包含最多128的值.如果您将整数n包装两次,如果值很小,则每次都会得到相同的整数引用足以进入缓存;否则,您将获得两个单独的实例.
目前,您取消装入旧值,计算新值,并在调用compareAndSet()时再次装入旧值.一旦达到128,就会停止获取缓存值,因此第二个盒装副本不再与AtomicReference中的副本相同.