Является ли цикл foreach Java над неизменяемым энергозависимым массивом безопасным для потоков? - PullRequest
0 голосов
/ 14 октября 2018

У меня есть изменчивая ссылка на неизменный массив, который асинхронно изменяется путем замены ссылки новой версией.Гарантируется ли потокобезопасность при итерации с 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);
  }
}

Почему я задаю этот вопрос?

Обычно цикл foreach в Java расширяется до Iterator:

Iterator<String> i = m_array.iterator();
while(i.hasNext())
  ...

В этом случае только один доступ к m_array, эффективно делающий атомный снимок.Так что все в порядке.

Но что, если будущая реализация Java оптимизирует foreach для необработанных массивов, поскольку в этом случае итераторы работают довольно медленно?(см. foreach против производительности )

Реализация может генерировать код, подобный

for (int i = 0; i < m_array.length; i++)
{ String s = m_array[i];
  ...

Это больше не потокобезопасно из-за множественного доступа к m_array.В этом случае временная переменная со снимком m_array требуется, когда поле является изменчивым.

Гарантируется, что вышеописанная оптимизация никогда не произойдет таким образом, и мой пример кода гарантированно безопасен?

1 Ответ

0 голосов
/ 14 октября 2018

Да, использование расширенного цикла for для ссылки на энергозависимый массив является поточно-ориентированным в отношении асинхронных изменений энергозависимого поля.

Обоснование

Но что, если будущая реализация Java оптимизирует foreach для необработанных массивов, поскольку итераторы в этом случае работают довольно медленно?

Iterator s не используются для улучшенных циклов для массивов, только для Iterables.Спецификация языка Java гарантирует, что каждый доступ к массиву в цикле находится в одном и том же экземпляре для каждой итерации, даже если значение m_array изменилось во время итерации.Расширенный цикл for для массива указывается с использованием следующего эквивалентного кода:

T[] #a = Expression;
L1: L2: ... Lm:
for (int #i = 0; #i < #a.length; #i++) {
    VariableModifiersopt TargetType Identifier = #a[#i];
    Statement
} 

См. 14.14.2 .Ключевым моментом здесь является то, что Expression, в вашем случае this.m_array, оценивается только один раз .

Важно подчеркнуть, что итерация использует ссылку на массив, а неего копия, так что возможно, что элементы в массиве могут измениться во время итерации.Однако из вашего примера кода я предполагаю, что вы знаете, что это не может произойти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...