使用同步时不需要原子引用

Atomic references are unnecessary when using synchronized

此代码来自 Fred Long 的书 Java Concurrency Guidelines。我明白一组原子操作不是原子操作。所以下面的代码是不合规的。要查找代码,请查看第 23 页。

public class Adder {

    private AtomicReference<BigInteger> first;
    private AtomicReference<BigInteger> second;

    public Foo(BigInteger f, BigInteger s) {
        first = new AtomicReference<BigInteger>(f);
        second = new AtomicReference<BigInteger>(s);
    }

    public void update(BigInteger f, BigInteger s) {
        first.set(f);
        second.set(s);
    }

    public BigInteger add() {
        return first.get().add(second.get());
    }
}

正确的解决方案如下所示:

final class Adder {
    // ...
    public synchronized void update(BigInteger f, BigInteger s){
        first.set(f);
        second.set(s);
    }

    public synchronized BigInteger add() {
        return first.get().add(second.get());
    }
}

但我认为正确解决方案中的原子引用是多余的,因为 synchronized 保证了可见性和原子性。

所以我的解决方案如下所示:

public class Addrer {

    private BigInteger first;
    private BigInteger second;

    public Addrer(BigInteger f, BigInteger s) {
        first = f;
        second = s;
    }

    public synchronized void update(BigInteger f, BigInteger s) {
        first = f;
        second = s;
    }

    public synchronized BigInteger add() {
        return first.add(second);
    }
}

我说得对吗?

您需要将 firstsecond 字段设为私有并将这些值公开为同步方法。否则直接读取字段可能会导致来自 BigInteger 对象的数据过时或部分过时(非易失性字段读取不是线程安全的)。那么你的 class 将是线程安全的。

您可能会尝试使这些字段可变,但它不能保证您的 updateadd 方法的原子性,因为一个线程可能会在您的 [=14] 中间更新一个字段=] 或 add 方法在另一个线程中执行。

public class Adder {
    private BigInteger first;
    private BigInteger second;

    public Adder(BigInteger f, BigInteger s) {
        first = f;
        second = s;
    }

    public synchronized BigInteger getFirst() {
        return first;
    }

    public synchronized BigInteger getSecond() {
        return second;
    }

    public synchronized void update(BigInteger f, BigInteger s) {
        first = f;
        second = s;
    }

    public synchronized BigInteger add() {
        return first.add(second);
    }
}