Multithreading SourceCode Java Code

Java通过Updater保证变量线程安全

Posted on 2022-05-28,3 min read
  • 在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);
    }
    

下一篇: Spring事务源码简单分析→

loading...
��