Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Concurrent Reads from Unmodfiable Map

If I statically initialize a map and set the reference to a Collections.unmodifiableMap(Map m). Do I need to synchronize reads?

private static final Map<String,String> staticMap; 
static{
   Map<String,String> tempMap = new HashMap<String,String>();

   tempMap.put("key 1","value 1");
   tempMap.put("key 2","value 2");
   tempMap.put("key 3","value 3");

   staticMap = Collections.unmodifiableMap(tempMap);
}
like image 338
nsfyn55 Avatar asked Apr 22 '11 13:04

nsfyn55


People also ask

Is Unmodifiable map thread-safe?

unmodifiableMap(deliverersMod); as well as the preceding operations where the map is populated. So your code is thread safe and your getDeliverers method will return a result based on the latest version of your map.

Is HashMap is thread-safe?

HashMap is non-synchronized. It is not thread-safe and can't be shared between many threads without proper synchronization code whereas Hashtable is synchronized.

What is ConcurrentHashMap in Java 8?

Java 8 provides Stream support in the ConcurrentHashMap as well. Unlike most stream methods, the bulk (sequential and parallel) operations allow concurrent modification safely. ConcurrentModificationException won't be thrown, which also applies to its iterators.


6 Answers

No, the map you're creating there is effectively immutable (since nothing has a reference to the mutable backing map) and safe for concurrent access. If you want a clearer guarantee of that along with making it easier to create the map, Guava's ImmutableMap type is designed for just this sort of use (among other things):

private static final ImmutableMap<String, String> staticMap = ImmutableMap.of(
    "key1", "value1",
    "key2", "value2",
    "key3", "value3");
like image 179
ColinD Avatar answered Sep 30 '22 19:09

ColinD


Nope, reads don't modify the map so i wouldn't worry about it at all. it's only the write+write or write+read that requires synchronization around it.

like image 36
Liv Avatar answered Sep 30 '22 17:09

Liv


It depends on the Map implementation. HashMap, TreeMap and the like all have reads that are modification free and so are fine, but implementations that track usage may perform updates internally.

An example is a LinkedHashMap with access ordering:

new LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)

This will actually reorder the elements on each read such that iteration over the keys, values or entries will be in last accessed first order. Another map that may modify is WeakHashMap.

An excellent alternative \would be the ImmutableMap found in Google's guava library.

like image 37
Jed Wesley-Smith Avatar answered Sep 30 '22 19:09

Jed Wesley-Smith


I disagree with the above answers. Contained in the map implementations are non-volatile fields (like HashMap.entrySet. In the unmodifiable case: UnmodifiableMap.keySet, UnmodifiableMap.entrySet and UnmodifiableMap.values). These fields are lazily initialized, so NULL after the static initializer. If one thread then calls entrySet(), this initializes the entrySet field. Access to this field is unsafe from all other threads. The field may be seen from another thread in an inconsistent state or not at all.

like image 42
Reto Aschwanden Avatar answered Sep 30 '22 18:09

Reto Aschwanden


The short answer is: no. You don't need to lock if there is no read-write contention. You only lock if whatever you're sharing might change, if it doesn't change then it's basically immutable and immutables are considered thread safe.

like image 38
Kiril Avatar answered Sep 30 '22 18:09

Kiril


I think others have covered the answer already (yes in the case of the HashMap implementation). If you don't necessarily always need the map to be created, you can make it lazy using the Initialize-On-Demand Holder idiom:

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class YourClass {

  // created only if needed, thread-safety still preserved
  private static final class MapHolder {
    private static final Map<String,String> staticMap; 
    static{
      System.out.println("Constructing staticMap");
      Map<String,String> tempMap = new HashMap<String,String>();

      tempMap.put("key 1","value 1");
      tempMap.put("key 2","value 2");
      tempMap.put("key 3","value 3");

      staticMap = Collections.unmodifiableMap(tempMap);
    }
  }

  // use this to actually access the instance 
  public static Map<String,String> mapGetter() {
    return MapHolder.staticMap;
  }

  public static void main(String[] arg) {
    System.out.println("Started, note that staticMap not yet created until...");
    Map<String,String> m = mapGetter();
    System.out.println("we get it: " + m);
  }
}

which will print:

Started, note that staticMap not yet created until...
Constructing staticMap
we get it: {key 1=value 1, key 2=value 2, key 3=value 3}
like image 27
Alex Miller Avatar answered Sep 30 '22 19:09

Alex Miller