I have a volatile reference to an immutable array that is changed asynchronously by replacing the reference with a new version. Is it guaranteed to be thread-safe when iterating with foreach over this array?
Example:
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);
}
}
Why I ask this Question?
Normally the foreach loop in Java expands to an Iterator
:
Iterator<String> i = m_array.iterator();
while(i.hasNext())
...
In this case there is only one access to m_array effectively taking an atomic snapshot. So everything is fine.
But what if a future Java implementation optimizes foreach for raw arrays because iterators are quite slow in this case? (see foreach vs. for performance)
The implementation might generate code like
for (int i = 0; i < m_array.length; i++)
{ String s = m_array[i];
...
This is no longer thread-safe, because of multiple accesses to m_array
.
In this case a temporary variable with a snapshot of m_array
is required when the field is volatile.
Is the above optimization guaranteed never to happen in this way and my code example guaranteed to be safe?
Yes, using an enhanced for loop on a volatile array reference is thread-safe with respect to asynchronous changes of the volatile field.
But what if a future Java implementation optimizes foreach for raw arrays because iterators are quite slow in this case?
Iterator
s are not used for enhanced for loops for arrays, only for Iterable
s. The Java Language Specification guarantees that every array access within the loop is on the same instance for every iteration, even if the value of m_array
has changed during the iteration. An enhanced for loop for an array is specified using the following equivalent code:
T[] #a = Expression;
L1: L2: ... Lm:
for (int #i = 0; #i < #a.length; #i++) {
VariableModifiersopt TargetType Identifier = #a[#i];
Statement
}
See 14.14.2. The key point here is that Expression
, in your case this.m_array
, is only evaluated once.
It is important to stress that the iteration uses a reference to the array, not a copy of it, so it is possible that the elements in the array could change during the iteration. However I assume from your code example that you know this can't happen.