Tam*_*más 10 java concurrency immutability thread-safety java-memory-model
我有一个关于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];
}
}
Run Code Online (Sandbox Code Playgroud)
据我所知,JMM保证在构造之后其他线程可以看到最终字段的值.但是我希望确保其他线程在构建之后能够看到存储在数组中的最新数据版本.
当然上面的代码只是一个呈现问题的简单示例,实际上我想为直接字节缓冲区实现一个简单的缓存,我不想依赖一些Collection类.目前我正在使用ReentrantReadWriteLock来确保正确的行为,但如果可能的话我想避免它.
And*_*yle 11
在这个例子中,一切都会好的(嗯,让我们暂停判断).在线程安全方面,不变性是ambrosia - 如果一个值不能改变,大多数并发问题立即不再是一个问题.
Amir提到volatile它通常很有用 - 但是构造函数对于final确保可见性的变量也有类似的语义.有关详细信息,请参阅JLS第17.5节 - 实质上,构造函数构成了对最终变量的写入与任何后续读取之间的先发生关系.
编辑:所以你在构造函数中设置对数组的值引用,它在那一点的所有线程中都是可见的,然后它不会改变.所以我们知道所有其他线程都会看到相同的数组.但阵列的内容怎么样?
就目前而言,数组元素在波动性方面没有任何特殊的语义,它们就好像你只是自己声明了一个类:
public class ArrayTen {
private int _0;
private int _1;
// ...
private int _9;
public int get(int index) {
if (index == 0) return _0;
// etc.
}
}
Run Code Online (Sandbox Code Playgroud)
所以 - 如果我们可以做一些事情来建立发生在之前的关系,另一个线程将只看到这些变量.如果我的理解是正确的,这只需要对原始代码进行一些小改动.
我们已经知道数组引用的设置发生在构造函数结束之前.另一个始终正确的观点是,一个线程中的操作发生在同一个线程中的后续操作之前.因此,我们可以通过首先设置数组字段,然后分配最终字段来组合这些,以便获得可见性的传递保证.这当然需要一个临时变量:
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.
}
Run Code Online (Sandbox Code Playgroud)
我认为这是安全的,因为我们已经改变了看似无关的任务和人口顺序.
但同样,可能还有一些我错过的东西,这意味着并发保证不像希望的那样强大.这个问题在我看来是一个很好的例子,为什么编写防弹多线程代码是棘手的,即使你认为你做的事情非常简单,以及如何做出正确的思考和谨慎(以及错误修正).
| 归档时间: |
|
| 查看次数: |
1705 次 |
| 最近记录: |