Is an immutable EnumMap thread safe?

Could an immutable map built on top of a EnumMap be used in a multi-thread environment or is there any risk of concurrency issues?

public enum MyEnum {
    VALUE1, VALUE2, VALUE3, VALUE4;
}

private final Map<MyEnum, BObject> myMap;

public MyClassConstructor() {
    EnumMap<MyEnum , BO> mMap = new EnumMap<>(MyEnum.class);
    mMap.put(MyEnum.VALUE1, new ImmutableBO1()); // imutavel
    mMap.put(MyEnum.VALUE2, new ImmutableBO2());
    // etc 
    myMap = Collections.unmodifiableMap(mMap);
    mMap = null; // ilustrativo, todas referencias a mMap são descartadas 
}
 13
Author: Maniero, 2013-12-24

1 answers

What is a thread-Safe object?

Is an object that, in a given context of use, ensures secure access to data shared by multiple threads without unwanted side effects.

These side effects usually occur when one or more threads try to modify the same dataset at the same time.

Let's assume that we have an object with a counter shared between several threads :

int atual = 0;
public int incrementa() {
    atual++;
    return atual;
}

Now suppose that two threads T1 and T2 call at the same time the method incrementa() and the following execution sequence occurs:

  1. process T1 executes Line atual++ (total = = 1)
  2. process T2 executes Line atual++ (total = = 2)
  3. process T2 returns the value of total, i.e. 2
  4. process T1 returns the value of total, i.e. 2

Obviously this will cause a side effect unwanted.

One solution is to synchronize the method or block that handles the shared data, for example:

int atual = 0;
public synchronized int incrementa() {
    atual++;
    return atual;
}

In this second example, the two threads would not run the incrementa() method at the same time on the same object. Only one at a time would enter the block with increment, so this would avoid the side effect.

It is important to note that the synchronization in the method is equivalent to a block synchronized(this), which means that the block is done on the object and if there are other synchronized methods, there may be a bottleneck. A more suitable way for synchronization would be:

Integer atual = 0;
public Integer incrementa() {
    synchronized (atual) {
        atual++;
        return atual;
    }
}

In this simple example, the above code does not bring many advantages. But the good practice of limiting the scope of synchronization to the maximum makes for fewer unnecessary blockages, which makes a lot of difference in slightly more complex scenarios.

Note : the above example is for educational purposes only. Care must be taken when synchronizing a instance of Integer because in some situations Java uses a cache of integers from -127 to +127. Note that this would not occur if we used new Integer(n), but only methods like valueOf() or getInteger(). A commonly used practice, including in standard Java libraries, is to create an object instance and assign to some attribute of the class, so you can use that object as a unique semaphore.

What if there are no modifications?

Non-modifiable objects are, by nature, safe for use in multiple threads as there is no risk of side effects. See what Wikipedia says:

Immutable objects are often useful because they are inherently thread-safe.

Translation:

Immutable objects are usually useful because they are inherently thread-safe

Anyway, immutable objects do not need to be synchronized to be safely used in multithreading .

A common EnumMap is Thread-safe?

Not . See what the docuentation (javadoc says):

If multiple threads access an enum map concurrently, and at least one of the threads modifies the map, it should be synchronized externally.

Translation:

If multiple threads access an enum map concurrently, and at least one of them modifies the map, it must be synchronized externally.

An unmodifiable EnumMap is Thread-safe?

Yes. the answer turns out to be obvious after we understand the concepts presented above.

So there will never be problems with immutable objects?

It depends . As I said at the beginning, it depends on the context of use.

Just to cite an example, suppose an immutable map contains mutable ArrayList elements. Sixth threads different access and modify these lists, side effects will likely occur.

Also one must be careful with Java classes that are inherently thread-unsafe, such as the class SimpleDateFormat. This class extends the class DateFormat, which has an attribute of type Calendar, which is mutable. Incidentally, having attributes of type Calendar can be a problem in multi-threading scenarios, since it is changeable.

In summary, analyze all objects shared by threads, including their contents.

What if I need a thread-Safe map? Should I create a wrapper with multiple blocks synchronized?

Java provides synchronized maps for various tastes. Let's look at some:

  • ConcurrentHashMap: a" new " version of the synchronized map with respect to HashTable, supporting manipulation by multiple threads, but without blocking read.
  • Collections.synchronizedMap(map): returns a wrapper synchronized over the map, but even read methods are synchronized, resulting in more bottlenecks with respect to ConcurrentHashMap.

Class references and wrappers synchronized and immutable

The list of synchronized wrappers is:

public static <T> Collection<T> synchronizedCollection(Collection<T> c);
public static <T> Set<T> synchronizedSet(Set<T> s);
public static <T> List<T> synchronizedList(List<T> list);
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m);
public static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s);
public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m);

The list of immutable wrappers is:

public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c);
public static <T> Set<T> unmodifiableSet(Set<? extends T> s);
public static <T> List<T> unmodifiableList(List<? extends T> list);
public static <K,V> Map<K, V> unmodifiableMap(Map<? extends K, ? extends V> m);
public static <T> SortedSet<T> unmodifiableSortedSet(SortedSet<? extends T> s);
public static <K,V> SortedMap<K, V> unmodifiableSortedMap(SortedMap<K, ? extends V> m);

The official reference of methods that return wrappers immutable and synchronized is here .

Synchronized versions of collection classes are found in the java.util.concurrent package. Here are some substitutes for conventional classes:

 14
Author: utluiz, 2020-06-11 14:45:34