2

In vavr you have the io.vavr.collection.Set that is immutable. What is a proper way and idiomatic way to use that considering that the addName() and names() could be called from various threads?

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;
  }
}

Should I use volatile? Should I use AtomicRef<Set<String>> instead?

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
raisercostin
  • 8,777
  • 5
  • 67
  • 76
  • 1
    Replacing the immutable variable with another means you still have mutable state. If you have mutable state, it's not a functional approach. If you're not using a functional approach, maybe it would be just better to use `ConcurrentHashMap.newKeySet()`? – Krzysztof Atłasik May 22 '19 at 11:22
  • What I like about the functional collections is that the iterators are just working and that you don't need to clone the collection before passing it around. – raisercostin Mar 18 '20 at 14:54

1 Answers1

3

I would use AtomicReference. See my answer to a similar question. Volatile is definitely not enough, because it only guarantees that updates to the variable will be visible to other threads immediately (it effectively disables cached access to it). Concurrent thread access will not be synchronized though, so it could happen that two threads build an updated Set concurrently, and one of the threads will overwrite the other threads change.

Given two threads T1 and T2 that are running concurrently, imagine the following sequence of events:

  1. T1 reads variable, current state is V
  2. T2 reads variable, current state is V
  3. T1 calculates updated state V×U1
  4. T2 calculates updated state V×U2
  5. T1 updates variable to V×U1
  6. T2 updates variable to V×U2

The final value from the above sequence will be V×U2, so the update U1 is effectively lost.

AtomicReference on the other hand guarantees that the variable is updated atomically. You'll have to pass in the updater function to the AtomicReference, so it will be called before atomically storing the result. Make sure you are using a pure function without side effects as the updater function might be called multiple times in case the reference was updated atomically by another thread in the meanwhile.

Nándor Előd Fekete
  • 6,988
  • 1
  • 22
  • 47