简体   繁体   English

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

[英]Can a Java collection be safely used outside of synchronized() after initializing in a synchronized() block in the same function?

Is the following code considered thread-safe, ie: is the write to list guaranteed to happen-before the read to list? 下面的代码是否被认为是线程安全的,即:是否保证在读取列表之前发生写入列表? I've been trying to understand whether this would be considered safe in the Java memory model, but it's unclear. 我一直试图了解这在Java内存模型中是否会被认为是安全的,但目前还不清楚。

Through basic flow analysis, it looks like it is guaranteed that all possible threads will have to pass through the synchronized initializer block before hitting the for loop below, but will iteration over that list be deterministic and thread-safe? 通过基本的流分析,看起来保证所有可能的线程必须在下面的for循环之前通过synchronized初始化块,但是对该列表的迭代是确定性的还是线程安全的? I'm not sure that initialization is guaranteed to happen-before the use of list below. 在使用下面的列表之前 ,我不确定是否可以保证初始化。

Assume that this is the only method in the class. 假设这是该类中唯一的方法。 I know that moving iteration inside the synchronized block would guarantee thread-safety, but I'm more interested in knowing whether this construct is safe. 我知道在synchronized块中移动迭代会保证线程安全,但我更想知道这个构造是否安全。

Also, assume that the list never escapes the class. 此外,假设列表永远不会逃避类。

The Java Memory Model is explained in the JLS here: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4 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
    }
}

It's thread-safe if and only if this is the only place where you structurally modify the list. 当且仅当这是您在结构上修改列表的唯一地方时,它是线程安全的。

If you modify the list somewhere else (for example using clear() ) somewhere else, even if that other place uses synchronized as well , then the list could easily be modified while you are iterating over it . 如果你在其他地方修改列表(例如使用clear() ), 即使其他地方也使用了synchronized ,那么在迭代它时可以很容易地修改列表。

If you do not intend to modify the list anywhere else, then using Collections.unmodifiableList() to ensure (and document) this fact is probably a good idea. 如果您打算在其他地方修改列表,那么使用Collections.unmodifiableList()来确保(并记录)这个事实可能是一个好主意。

The JLS #17.4.5 guarantees that: JLS#17.4.5保证:

An unlock on a monitor happens-before every subsequent lock on that monitor. 监视器上的解锁发生在该监视器上的每个后续锁定之前。

It also guarantees that the synchronized block can't be executed concurrently and can't be reordered with the for loop. 它还保证同步块不能同时执行,也不能用for循环重新排序。

So the first thread that arrives and gets the monitor (let's call it T0) will initialise the list. 因此,第一个到达并获取监视器的线程(让我们称之为T0)将初始化列表。 When T0 exits the synchronized block, the intra-thread semantics guarantee that the for loop will execute as expected in T0. 当T0退出同步块时,线程内语义保证for循环将在T0中按预期执行。

All threads that arrive subsequently will wait for the monitor to be available, acquire it and because of the guarantee above will see the list as it was initialised by T0 (ie not null and populated). 随后到达的所有线程将等待监视器可用,获取它并且由于上面的保证将看到由T0初始化的列表(即,不为空并且已填充)。 And because of intra-thread semantics, the for loop will execute as expected. 并且由于线程内语义,for循环将按预期执行。

Conclusion : your code is safe if your list is not written to at other places and all reads are done after acquiring the monitor. 结论 :如果您的列表未写入其他位置并且所有读取都在获取监视器后完成,则您的代码是安全的。

If you never modify your list again appart from within that synchronized method, the code you exposed is safe. 如果您从未在同步方法中再次修改列表,则您公开的代码是安全的。 Now if some other method (not bar()) uses that same list, then your code is not safe. 现在,如果某个其他方法(不是bar())使用相同的列表,那么您的代码就不安全了。 Also, you should declare final List list 此外,您应该声明final List list

The synchronized(list) only applies to the block of code contained therein. synchronized(list)仅适用于其中包含的代码块。 If another thread modifies the list while you iterate through the list using the for each loop you will have a problem. 如果另一个线程在使用for each循环遍历列表时修改列表,则会出现问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM