Поддерживать ссылку, а затем назначить потокобезопасный новый объект?

#java #multithreading #concurrency

#java #многопоточность #параллелизм

Вопрос:

У меня есть статический массив int, который является общим для потоков:

 static int[] res = new int[10];
  

В функции я хочу изолировать то, что находится в res, и, по сути, сбросить его значения до 0. Является ли следующий код потокобезопасным?

 final int[] copy = res;
res = new int[10];
// do sth with `copy`
  

Если нет, что может быть более разумным способом без снижения производительности (кроме использования AtomicInteger или Semaphore).

Ответ №1:

Это не потокобезопасно. Нет никакой гарантии, когда, если вообще когда-либо, другие потоки увидят изменение статической переменной res .

Вы могли бы изменить его на:

 static volatile int[] res = new int[10];
  

И тогда другие потоки гарантированно получат его при следующем использовании res переменной.

В данном конкретном случае, когда вы только сбрасываете значение до нуля и не зависите от предыдущего значения res , этого, вероятно, достаточно.

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

Однако одно предупреждение: другие потоки все еще могут манипулировать значениями в вашей переменной « copy «, поскольку они, возможно, извлекли ссылку и сохранили ее.

Ответ №2:

Небезопасно без какой-либо формы синхронизации.

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

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

Вы должны защитить все обращения (из любого потока) к res элементам массива с помощью блокировки.