And*_*rey 13 java multithreading
我有一个List,它可以在线程安全上下文中使用,也可以在非线程安全上下文中使用.它将是哪一个,是不可能事先确定的.
在这种特殊情况下,每当列表进入非线程安全上下文时,我都会使用它进行包装
Collections.synchronizedList(...)
但我不想包装它,如果不进入非线程安全的上下文.Fe,因为列表很大并且使用密集.
我读过Java,它的优化策略是严格的多线程 - 如果你没有正确地同步你的代码,它不能保证在线程间上下文中正确执行 - 它可以重新组织代码,提供一致性仅在一个线程的上下文中(参见http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.3).铁,
OP1; OP2; OP3;
可以重组
OP3; OP2; OP1;
,如果它产生相同的结果(在单线程上下文中).
现在我想知道,如果我
填写我的列表,然后再用synchronizedList包装它,
把它包起来,
然后由不同的线程使用
, - 是否有可能,不同的线程会看到此列表仅部分填充或根本未填充?JVM可能推迟(1)直到(3)之后?是否有一种正确而快速的方法使(大)List成为线程安全的非线程安全的?
Paŭ*_*ann 12
当您通过线程安全的方式将列表提供给另一个线程时(例如使用synchronized块,volatile变量或者AtomicReference),可以保证第二个线程看到整个列表处于传输时的状态(或任何更晚的状态)国家,但不是早期的国家).
如果之后不进行更改,则也不需要synchronizedList.
编辑(在评论之后,备份我的声明):
我假设如下:
我们有一个易变的变量list.
volatile List<String> list = null;
Run Code Online (Sandbox Code Playgroud)线程A:
list为指向L(这意味着写入Llist)样本来源:
public void threadA() {
List<String> L = new ArrayList<String>();
L.add("Hello");
L.add("World");
list = l;
}
Run Code Online (Sandbox Code Playgroud)线程B:
list样本来源:
public void threadB() {
List<String> K = list;
for(String s : K) {
System.out.println(s);
}
}
Run Code Online (Sandbox Code Playgroud)所有其他线程都不会触及列表.
现在我们有了这个:
于挥发性变量(§8.3.1.4)写v同步-与所有后续读取的v任何线程(其中,随后根据该同步命令被定义).
在之前发生阶是单个线程与所述同步命令的程序的命令的传递闭包.所以我们有:
1-A发生在2-A发生之前 - 在1-B发生之前 - 在2-B之前发生
因此1-A发生在2-B之前.
如果一个动作发生在另一个动作之前,则第一个动作在第二个动作之前可见并在第二个之前被命
所以我们的迭代线程真的可以看到整个列表,而不仅仅是它的某些部分.因此,使用单个volatile变量传输列表就足够了,在这种简单的情况下我们不需要同步.
关于线程A的程序顺序,还有一个编辑(这里,因为我有比注释中更多的格式自由).(我还在上面添加了一些示例代码.)
从JLS(部分程序顺序):
在由每个线程t执行的所有线程间动作中,t的程序顺序是反映根据t的线程内语义将执行这些动作的顺序的总顺序.
那么,线程A的线程内语义是什么?
有些段落上面:
内存模型确定可以在程序中的每个点读取哪些值.隔离中每个线程的操作必须遵循该线程的语义,除了每次读取所看到的值由内存模型确定.当我们参考这个时,我们说该程序遵循线程内语义.线程内语义是单线程程序的语义,允许基于线程内读取操作所看到的值完全预测线程的行为.为了确定执行中的线程t的动作是否合法,我们只是评估线程t的实现,因为它将在单线程上下文中执行,如本规范其余部分所定义.
本规范的其余部分包括第14.2节(块):
通过从第一个到最后一个(从左到右)按顺序执行每个局部变量声明语句和其他语句来执行块.
因此,程序顺序确实是程序源代码中给出语句/表达式的顺序.
因此,在我们的示例源中,内存操作创建一个新的ArrayList,添加"Hello",添加"World",并指定list(前三个由更多的子动作组成)确实是按此程序顺序.
(VM不必按此顺序执行操作,但此程序顺序仍然有助于发生之前的顺序,从而有助于其他线程的可见性.)