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

В vavr у вас есть неизменяемая коллекция io.vavr.collection.Set. Каков правильный и идиоматический способ использовать это, учитывая, что addName() и names() могут вызываться из разных потоков?

import io.vavr.collection.Set;
import io.vavr.collection.HashSet;
public class Names{
  public/*private*/ /**volatile*/ Set<String> names = HashSet.empty();
  public void addName(String name){
    names = names.add(name);
  }
  public Set<String> names(){
    return names;
  }
}

Стоит ли использовать летучие? Должен ли я использовать вместо этого AtomicRef<Set<String>>?


person raisercostin    schedule 20.05.2019    source источник
comment
Замена неизменной переменной на другую означает, что у вас все еще есть изменяемое состояние. Если у вас есть изменяемое состояние, это не функциональный подход. Если вы не используете функциональный подход, может быть, лучше было бы использовать ConcurrentHashMap.newKeySet()?   -  person Krzysztof Atłasik    schedule 22.05.2019
comment
Что мне нравится в функциональных коллекциях, так это то, что итераторы просто работают и вам не нужно клонировать коллекцию перед ее передачей.   -  person raisercostin    schedule 18.03.2020


Ответы (1)


Я бы использовал AtomicReference. См. Мой ответ на аналогичный вопрос. Volatile определенно недостаточно, потому что он гарантирует только то, что обновления переменной будут немедленно видны другим потокам (он фактически отключает кэшированный доступ к ней). Однако одновременный доступ к потоку не будет синхронизирован, поэтому может случиться так, что два потока создадут обновленный Set одновременно, и один из потоков перезапишет изменение других потоков.

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

  1. T1 читает переменную, текущее состояние - V
  2. T2 читает переменную, текущее состояние - V
  3. T1 вычисляет обновленное состояние V × U1
  4. T2 вычисляет обновленное состояние V × U2
  5. T1 обновляет переменную до V × U1
  6. T2 обновляет переменную до V × U2

Конечным значением из приведенной выше последовательности будет V × U2, поэтому обновление U1 фактически потеряно.

AtomicReference, с другой стороны, гарантирует, что переменная обновляется атомарно. Вам нужно будет передать функцию обновления в AtomicReference, поэтому она будет вызываться перед атомарным сохранением результата. Убедитесь, что вы используете чистую функцию без побочных эффектов, так как функция обновления может вызываться несколько раз, если в это время ссылка была обновлена ​​атомарно другим потоком.

person Nándor Előd Fekete    schedule 21.05.2019