非线程安全标准集合的安全发布

Safe publication of a not-threadsafe standard collection

我的理解是否正确,以下代码片段在发布本质上不是线程安全的标准集合方面是安全的(本例中为 HashSet),因为 none线程在内存屏障后修改集合:

class C {
    private final Set<String> strings;

    C(final Set<String> strings) {
        this.strings = new HashSet<>(strings);
    }

    void doSmthAsync() {
        new Thread() {
            @Override
            public void run() {
                for (final String s : strings) {
                    System.out.println(s);
                }
            }
        }.start(); // Disregard the 2nd memory barrier caused by Thread.start()
    }
}

下面这个是不是?

class C {
    private final Set<String> strings = new HashSet<>();

    C(final Set<String> strings) {
        this.strings.addAll(strings);
    }

    void doSmthAsync() {
        new Thread() {
            @Override
            public void run() {
                for (final String s : strings) {
                    System.out.println(s);
                }
            }
        }.start(); // Disregard the 2nd memory barrier caused by Thread.start()
    }
}

更新:上面的例子并不完全正确,因为实际上有两个内存屏障,而不是一个:

如果相反,在 doSmthAsync() 方法中,我们将集合提供给一个已经启动的线程(例如,来自后台线程池的线程),似乎消费者线程可能会看到它状态中的最终字段在初始化期间有(即在第二种情况下为空集)。

根据您更新的问题,addAll 中可能存在竞争条件,因为这发生在设置了 final 字段之后。这并不意味着您会看到这种竞争条件,因为它的行为是未定义的,并且 JVM 可以自由添加内存屏障。


在启动线程之前只读的任何内容都是线程安全的。

顺便说一句,您的示例是编写同一事物的两种方式,因此它们彼此一样线程安全。

JSR-133 食谱

http://gee.cs.oswego.edu/dl/jmm/cookbook.html

A store of a final field (inside a constructor) and, if the field is a reference, any store that this final can reference, cannot be reordered with a subsequent store (outside that constructor) of the reference to the object holding that field into a variable accessible to other threads. For example, you cannot reorder x.finalField = v; ... ; sharedRef = x; This comes into play for example when inlining constructors, where "..." spans the logical end of the constructor. You cannot move stores of finals within constructors down below a store outside of the constructor that might make the object visible to other threads.

看起来两个例子在安全发布方面是等效的。