在同一函数的synchronized()块中初始化之后,是否可以在synchronized()之外安全地使用Java集合?

sli*_*eed 2 java concurrency thread-safety

下面的代码是否被认为是线程安全的,即:是否保证在读取列表之前发生写入列表?我一直试图了解这在Java内存模型中是否会被认为是安全的,但目前还不清楚.

通过基本的流分析,看起来保证所有可能的线程都必须synchronizedfor下面的循环之前通过初始化程序块,但是对该列表的迭代是确定性的还是线程安全的?在使用下面的列表之前,我不确定是否可以保证初始化.

假设这是该类中唯一的方法.我知道在synchronized块中移动迭代会保证线程安全,但我更想知道这个构造是否安全.

此外,假设列表永远不会逃避类.

Java内存模型在JLS中有解释:http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4

private List<Foo> list;
private final Object monitor = new Object();

public void bar() {
    synchronized (monitor) {
        if (list == null) {
            list = new ArrayList<>();
            list.add(...); // expensive operation
            list.add(...); // expensive operation
            list.add(...); // expensive operation
        }
    }

    for (Foo foo : list) {
        // do something with foo
    }
}
Run Code Online (Sandbox Code Playgroud)

Joa*_*uer 7

当且仅当这是您在结构上修改列表的唯一地方时,它是线程安全的.

如果你在其他地方修改列表(例如使用clear()),即使其他地方也使用synchronized,那么在迭代它时可以很容易地修改列表.

如果您打算在其他任何地方修改列表,那么使用Collections.unmodifiableList()确保(并记录)这个事实可能是一个好主意.