在Java中我们常常会设计到多线程并发的问题,如有一个Money类,里面有一个方法为addMoney,当我们需要需要对这个分发并发的时候,就会出现线程安全问题。对此,我们的解决方案一般有两个,加锁或CAS。
比如这样:原子Int,CAS更新
1
2
3
4
5
6
7
8
9
10
11
12
13public 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();
}
}或者这样:加锁保证线程安全
1
2
3
4
5
6
7
8
9
10
11
12
13public 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
这样一个类,来进行变量的更新。于是我们程序就可以修改成这样:
1
2
3
4
5
6
7public 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15private 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
1
2
3public final int addAndGet(int delta) {
return U.getAndAddInt(this, VALUE, delta) + delta;
}正常情况下,一般通过使用原子字段就行了,但是对于某些框架,当我们想对其中的一些volatile变量进行线程安全的更新的话,就可以使用Updater来更新,这可能也是设计这个类的原因吧。
1
2
3
4
5
6
7
8
9public 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);
}