【发布时间】:2018-10-14 10:52:38
【问题描述】:
我有一个对不可变数组的可变引用,该数组通过将引用替换为新版本来异步更改。 在这个数组上使用 foreach 迭代时,它是否保证是线程安全的?
例子:
class MyClass
{
volatile String[] m_array = new String[0];
public synchronized void add(String n)
{ m_array = ArrayUtils.add(m_array, n); // atomic replace
}
public void iterate() // not synchronized!
{ // Do something with each element
for (String s : m_array)
System.out.println(s);
}
}
我为什么要问这个问题?
Java 中的 foreach 循环通常会扩展为 Iterator:
Iterator<String> i = m_array.iterator();
while(i.hasNext())
...
在这种情况下,只有一次访问 m_array 可以有效地拍摄原子快照。所以一切都很好。
但是,如果未来的 Java 实现 优化 foreach 用于原始数组,因为在这种情况下迭代器非常慢,该怎么办? (见foreach vs. for performance)
实现可能会生成类似的代码
for (int i = 0; i < m_array.length; i++)
{ String s = m_array[i];
...
这不再是线程安全的,因为对m_array 的多次访问。
在这种情况下,当字段为 volatile 时,需要一个快照为 m_array 的临时变量。
是否保证上述优化永远不会以这种方式发生并且我的代码示例保证是安全的?
【问题讨论】:
-
您说“只有一次访问 m_array”。你认为迭代器做了什么?它在整个循环期间访问 m_array。直接访问原始数组的优化不会有太大的不同。
-
附带说明一下,Java 有一个 List 类型,它的工作方式与您想象的一样:
CopyOnWriteArrayList -
@Rudi Velthuis 不,迭代器根本不访问
m_array。它只访问 由m_array引用的数组。这是安全的,因为后者是不可变的。
标签: java arrays foreach volatile