3

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?

Marcel
  • 1,688
  • 1
  • 14
  • 25
  • You say "only one access to m_array". What do you think the iterator does? It accesses m_array during the entire loop. An optimization that accesses the raw array directly would not be much different. – Rudy Velthuis Oct 14 '18 at 11:02
  • 1
    As a side note, Java has a List type that works like you'd think: [`CopyOnWriteArrayList`](https://docs.oracle.com/javase/10/docs/api/java/util/concurrent/CopyOnWriteArrayList.html) – Powerlord Oct 14 '18 at 11:05
  • 1
    @Rudi Velthuis No, the iterator does not access `m_array` at all. It only accesses the array *referenced by* `m_array`. This is safe because the latter is immutable. – Marcel Oct 14 '18 at 13:41

1 Answers1

4

Yes, using an enhanced for loop on a volatile array reference is thread-safe with respect to asynchronous changes of the volatile field.

Rationale

But what if a future Java implementation optimizes foreach for raw arrays because iterators are quite slow in this case?

Iterators are not used for enhanced for loops for arrays, only for Iterables. 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.

Marcel
  • 1,688
  • 1
  • 14
  • 25
cpp beginner
  • 512
  • 6
  • 23