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

2022-09-02 09:30:08

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

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

}

编辑:我的错是没有澄清这个问题。根据JMM常见问题解答

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

这让我感到困惑,aMap的集合是。所以其他线程可以看到这些aMap = new HashMap<String, String>();

aMap.put("1", "a");
aMap.put("2", "b");
aMap.put("3", "c");

还是没有?

编辑:我发现这个问题完全接近我的问题


答案 1

如前所述,它是绝对线程安全的,并且由于其内存可见性效应而在这里很重要。final

存在保证,即在构造函数完成后,其他线程将在没有任何外部同步的情况下在映射中看到值。如果没有它,则无法保证在所有情况下都得到保证,并且在使新构造的对象可用于其他线程时,您需要使用安全发布习语,即(来自Java并发实践):finalfinal

  • 从静态初始值设定项初始化对象引用;
  • 将对它的引用存储到易失性字段或原子引用中;
  • 将对它的引用存储到正确构造的对象的最终字段中;或
  • 将对它的引用存储到由锁正确保护的字段中。

答案 2

是的,它是。无法修改引用本身,也无法在构造函数之后添加到映射中(除非反射)。aMap

如果公开它,则不会,因为两个线程可以同时修改映射。aMap

您可以通过 Collections.unmodifiableCollection 或 Collections.unmodifiableMap 使 unmodifiableMap 不可修改来改进您的类。aMap


推荐