Java并发:是最终字段(在构造函数中初始化)是否是线程安全的?

rob*_*mag 17 java concurrency java-memory-model

谁能告诉我这个类是否是线程安全的?

class Foo {

    private final Map<String,String> aMap;

    public Foo() {
        aMap = new HashMap<String, String>();
        aMap.put("1", "a");
        aMap.put("2", "b");
        aMap.put("3", "c");
    }

    public String get(String key) {
        return aMap.get(key);
    }

}
Run Code Online (Sandbox Code Playgroud)

编辑:我不能澄清这个问题.根据JMM FAQ:

应提供初始化安全性的新保证.如果一个对象被正确构造(这意味着对它的引用在构造期间不会被转义),那么看到对该对象的引用的所有线程也将看到在构造函数中设置的最终字段的值,而不需要同步.

这让我把这个集合混淆为aMap aMap = new HashMap<String, String>();.所以其他线程可以看到这些

aMap.put("1", "a");
aMap.put("2", "b");
aMap.put("3", "c");
Run Code Online (Sandbox Code Playgroud)

或不 ?

编辑:我发现这个问题与我的问题完全不同

axt*_*avt 19

正如已经指出的那样,它绝对是线程安全的,并且final由于其内存可见性效果而在这里很重要.

的存在final保证其他线程将看到值地图后构造完成,无需任何外部同步.如果没有final它就无法在所有情况下得到保证,并且在将新构造的对象提供给其他线程时,您需要使用安全的发布惯用法,即(来自Java Concurrency in Practice):

  • 从静态初始化程序初始化对象引用;
  • 将对它的引用存储到volatile字段或AtomicReference中;
  • 将对它的引用存储到正确构造的对象的最终字段中; 要么
  • 将对它的引用存储到由锁正确保护的字段中.


hvg*_*des 7

是的.无法修改引用aMap本身,或在构造函数后添加到地图(禁止反射).

如果你公开aMap它将不会,因为两个线程可以同时修改地图.

您可以aMap通过Collections.unmodifiableCollectionCollections.unmodifiableMap进行不可修改来改进您的类.