1.25 Java中的+ =运算符是线程安全的吗?| Java Debug 笔记

264 阅读2分钟

本文正在参加「Java主题月 - Java Debug笔记活动」,详情查看 活动链接

Debug 笔记 <Java中的+ =运算符是线程安全的吗?>

提问

我找到了以下Java代码。

for (int type = 0; type < typeCount; type++)
    synchronized(result) {
        result[type] += parts[type];
    }
}

我知道基本类型的基本操作是线程安全的,但是我不确定+=。如果上述synchronized是必要的,

是否可能有更好的类来处理这种操作?

回答一

否。

+=操作不是线程安全的。对于涉及分配给共享字段或数组元素的任何表达式来说,它要求锁定和/或适当的“先于”关系链是线程安全的。

(在字段声明为volatile的情况下,“先发生”关系存在...但是仅在读和写操作上存在。该+=操作由读和写组成。它们分别是原子的,但顺序不是。大多数使用的赋值表达式=涉及一个或多个读取(在右侧)和写入。该序列也不是原子的。)

回答二

尽管Java中没有AtomicDoubleAtomicDoubleArray

您仍然可以轻松地基于创建自己的Java AtomicLongArray

static class AtomicDoubleArray {
    private final AtomicLongArray inner;

    public AtomicDoubleArray(int length) {
        inner = new AtomicLongArray(length);
    }

    public int length() {
        return inner.length();
    }

    public double get(int i) {
        return Double.longBitsToDouble(inner.get(i));
    }

    public void set(int i, double newValue) {
        inner.set(i, Double.doubleToLongBits(newValue));
    }

    public void add(int i, double delta) {
        long prevLong, nextLong;
        do {
            prevLong = inner.get(i);
            nextLong = Double.doubleToLongBits(Double.longBitsToDouble(prevLong) + delta);
        } while (!inner.compareAndSet(i, prevLong, nextLong));
    }
}

正如你所看到的,我使用Double.doubleToLongBitsDouble.longBitsToDouble存储DoublesLongsAtomicLongArray。它们的位大小相同,因此精度不会丢失(-NaN除外,但我认为这并不重要)。

Java 8中,实现add可以更加轻松,因为您可以使用Java 1.8中添加的accumulateAndGet方法AtomicLongArray

回答三

32JVM中,即使是普通的“双精度”数据类型也不是线程安全的(因为它不是原子的),因为在Java中它占用了八个字节(涉及2 * 32位操作)。

文章翻译自Stack Overflow :stackoverflow.com/questions/3…