Java - 不可变数组线程安全

2023-12-31

我有一个关于 Java 内存模型的问题。这是一个呈现问题的简单类:

public class ImmutableIntArray {

    private final int[] array;

    public ImmutableIntArray() {
        array = new int[10];
        for (int i = 0; i < 10; i++) {
            array[i] = i;
        }
    }

    // Will always return the correct value?
    public int get(int index) {
        return array[index];
    }

}

据我所知,JMM 保证最终字段的值在构造后对其他线程可见。但我想确保其他线程在构造后会看到数组中存储的最新版本的数据。

当然,上面的代码只是一个简单的示例来说明问题,实际上我想为直接字节缓冲区实现一个简单的缓存,并且我不想依赖某些 Collection 类。目前我正在使用 ReentrantReadWriteLock 来确保正确的行为,但如果可能的话我希望避免它。


In this example, everything will be fine (hmm, let's suspend judgement a bit). Immutability is ambrosia when it comes to thread-safety - if a value cannot change, the majority of concurrency problems are immediately no longer a concern.

Amir https://stackoverflow.com/users/419075/amir-raminfar提及volatile这通常很有用 - 但构造函数也有类似的语义final确保可见性的变量。看JLS 条款 17.5 http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5详细信息 - 本质上构造函数形成一个发生在之前最终变量的写入与任何后续读取之间的关系。

EDIT: So you set the values reference to the array in the constructor, it's visible across all threads at that point, and then it doesn't change. So we know all other threads will see the same array. But what about the array's contents?

就目前情况而言,数组元素在波动性方面没有任何特殊语义,它们就像您自己声明了一个类,如下所示:

public class ArrayTen {
    private int _0;
    private int _1;
    // ...
    private int _9;

    public int get(int index) {
       if (index == 0) return _0;
       // etc.
    }
}

所以 - 如果我们可以做一些事情来建立另一个线程只会看到这些变量发生在之前关系。如果我的理解是正确的,这只需要对您的原始代码进行很小的更改。

我们已经知道数组引用的设置发生在之前构造函数的末尾。始终正确的另一点是,一个线程中的操作发生在之前同一线程中的后续操作。所以我们可以通过首先设置数组字段来组合这些,并且then分配最终字段,以获得可见性的传递保证。这当然需要一个临时变量:

public class ImmutableIntArray {

    private final int[] array;

    public ImmutableIntArray() {
        int[] tmp = new int[10];
        for (int i = 0; i < 10; i++) {
            tmp[i] = i;
        }
        array = tmp;
    }

    // get() etc.
}

我认为这肯定是安全的,因为我们已经改变了看似不相关的分配和人口顺序。

但同样,我可能还遗漏了其他一些东西,这意味着并发保证并不像希望的那么强大。在我看来,这个问题是一个很好的例子,说明了为什么编写防弹多线程代码很棘手,即使您认为自己正在做一些非常简单的事情,以及如何需要大量的思考和谨慎(然后是错误修复)才能正确。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java - 不可变数组线程安全 的相关文章

随机推荐