假设我声明了以下数组:
private final int[] array = new int[10];现在,如果我启动10个线程,每个线程都将一个值写入自己的键(线程1写到array[0],线程2写到array[1],等等),会不会有线程尝试缓存数组,或者所有更改都将在主内存中进行?
发布于 2018-11-27 12:21:05
线程可以缓存任何非易失性值.
特别是,线程的代码可以将线程不改变的boolean值内联到代码中。在重新编译更改之前,线程可能不会检测到更改。
在这种情况下,数组引用和数组中的值都将被缓存,但很可能会在一段时间后看到新值。
final的使用并没有真正的区别。原因之一是反射允许您更改final字段。
有这样一个线程安全数组的一个更干净的方法是使用Java5.0中添加的AtomicIntegerArray
final AtomicIntegerArray array = new AtomicIntegerArray(10);
public void increment(int n) {
array.incrementAndGet(n);
}发布于 2018-11-27 11:21:09
不,final没有单个数组元素的可见性语义,即使是易失性也只能保证引用突变本身上的在订货前发生,而不是元素。为此,您需要同步或使用CopyOnWriteArrayList。
发布于 2018-11-27 11:28:41
更新
final修饰符确保所有Thread-s都能看到该引用的初始化值。这一点很重要:final关键字只引用数组,而不引用存储在该数组中的值。
原始答案
我们不知道会发生什么。JVM可能缓存值,也可能不缓存值。我建议不要对此产生任何依赖。
我想你会想要一些能见度。通常,您可以使用volatile关键字或使用synchronization强制可见性。在这种情况下,您有一个数组if整数。
AtomicInteger-s数组
您可以用AtomicInteger -s数组替换它:
private final AtomicInteger[] array = new AtomicInteger[10];但在这种情况下,您必须以不同的方式操作这些值:
array[0] = new AtomicInteger(0);
array[0].incrementAndGet();并发列表
另一种方法是使用并发列表,如CopyOnWriteArrayList或使用Collections.synchronizedList()包装方法:
// option 1
private final List<Integer> list = Collections.synchronizedList(new ArrayList<>());
// option 2
private final List<Integer> list = new CopyOnWriteArrayList<>();无论哪种方式,您的访问器都将与array的访问器有所不同。列表初始化是最棘手的部分:
// this prevents all kinf of OutOfBoundsException-s
for (int i = 0; i < 10; i++) {
list.add(0);
}
// and now you can use it
list.set(0, list.get(0) + 1);并发映射
您还可以使用map,因此可以跳过有问题的初始化:
private final ConcurrentMap<Integer, Integer> map = new ConcurrentHashMap<>();
// ...
map.putIfAbsent(0, 0);
map.put(0, map.get(0) + 1);https://stackoverflow.com/questions/53498255
复制相似问题