-
在Java中我们常常会设计到多线程并发的问题,如有一个Money类,里面有一个方法为addMoney,当我们需要需要对这个分发并发的时候,就会出现线程安全问题。对此,我们的解决方案一般有两个,加锁或CAS。
-
比如这样:原子Int,CAS更新
public static class Money{ private final AtomicInteger value; public Money() { this.value = new AtomicInteger(0); } public void addMoney(int value){ this.value.addAndGet(value); } public int getMoney(){ return value.get(); } }
-
或者这样:加锁保证线程安全
public static class Money{ private int value; public Money() { this.value = 0; } public synchronized void addMoney(int value){ this.value += value; } public int getMoney(){ return value; } }
-
但是在阅读某些源码的时候,发现有些源码使用到了
AtomicIntegerFieldUpdater
这样一个类,来进行变量的更新。 -
于是我们程序就可以修改成这样:
public static class Money{ private volatile int value; private final AtomicIntegerFieldUpdater<Money> updater = AtomicIntegerFieldUpdater.newUpdater(Money.class, "value"); public void addMoney(int value){ updater.addAndGet(this, value); } }
-
也能达到相同的效果。
- 有两个前提:
- value使用volatile修饰。
- 同一个updater更新才能保证线程安全问题。
- 上面两个前提很好理解。
- 有两个前提:
-
通过阅读源码,我们可以发现,这两个类底层都是调用的Unsafe来Cas进行更新的。
- AtomicIntegerFieldUpdater
private static final Unsafe U = Unsafe.getUnsafe(); public int addAndGet(T obj, int delta) { int prev, next; do { prev = get(obj); next = prev + delta; } while (!compareAndSet(obj, prev, next)); return next; } public final boolean compareAndSet(T obj, int expect, int update) { accessCheck(obj); return U.compareAndSetInt(obj, offset, expect, update); }
- AtomicInteger
public final int addAndGet(int delta) { return U.getAndAddInt(this, VALUE, delta) + delta; }
-
正常情况下,一般通过使用原子字段就行了,但是对于某些框架,当我们想对其中的一些volatile变量进行线程安全的更新的话,就可以使用Updater来更新,这可能也是设计这个类的原因吧。
public static class Money{ private volatile int value; } private final Money money = new Money(); private AtomicIntegerFieldUpdater<Money> updater = AtomicIntegerFieldUpdater.newUpdater(Money.class, "value"); public void addMoney(int value){ updater.addAndGet(money, value); }
阅读量
loading...