来自不可修改地图的并发读取

时间:2011-04-22 13:00:49

标签: java concurrency

如果我静态初始化地图并将引用设置为Collections.unmodifiableMap(Map m)。我是否需要同步读取?

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

6 个答案:

答案 0 :(得分:10)

不,您在那里创建的地图实际上是不可变的(因为没有任何引用可变支持地图)并且对并发访问是安全的。如果您想更清楚地保证这一点,并且更容易创建地图,GuavaImmutableMap类型就是为了这种用途而设计的(除此之外):

private static final ImmutableMap<String, String> staticMap = ImmutableMap.of(
    "key1", "value1",
    "key2", "value2",
    "key3", "value3");

答案 1 :(得分:5)

不,读取不要修改地图,所以我根本不用担心。它只是写+写或写+读取,需要围绕它进行同步。

答案 2 :(得分:4)

这取决于Map的实现。 HashMap,TreeMap等都具有免费修改的读取,因此很好,但跟踪使用情况的实现可能会在内部执行更新。

一个例子是具有访问顺序的LinkedHashMap:

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

这实际上会对每次读取时的元素进行重新排序,以便对键,值或条目的迭代将在最后一次访问的第一个顺序中进行。另一个可能修改的地图是WeakHashMap

一个很好的替代方案是Google的番石榴库中的ImmutableMap

答案 3 :(得分:4)

我不同意上述答案。地图实现中包含的是非易失性字段(如HashMap.entrySet。在不可修改的情况下:UnmodifiableMap.keySetUnmodifiableMap.entrySetUnmodifiableMap.values)。这些字段被懒惰地初始化,因此在静态初始化器之后为NULL。如果一个线程然后调用entrySet(),则初始化entrySet字段。从所有其他线程访问此字段是不安全的。可以从处于不一致状态的另一个线程看到该字段,或者根本不看到该字段。

答案 4 :(得分:3)

简短的回答是:不。如果没有读写争用,则无需锁定。只有当你共享的内容可能发生变化时才会锁定,如果它没有改变,那么它基本上是不可变的,并且不可变的被认为是线程安全的。

答案 5 :(得分:1)

我认为其他人已经覆盖了答案(在HashMap实现的情况下是的)。如果您不一定总是需要创建地图,可以使用Initialize-On-Demand Holder惯用法使其变得懒惰:

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

将打印:

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}