Java中变量赋值的可见性
我最近和一个朋友就这样的代码争论:Java中变量赋值的可见性,java,multithreading,concurrency,Java,Multithreading,Concurrency,我最近和一个朋友就这样的代码争论: import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * See memory consistency effects in a Java Executor. */ public class PrivateFieldInEnclosing { private long value; PrivateFieldInEnclo
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* See memory consistency effects in a Java Executor.
*/
public class PrivateFieldInEnclosing {
private long value;
PrivateFieldInEnclosing() {}
void execute() {
value = initializeValue();
ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(new Y());
}
class Y implements Runnable {
@Override
public void run() {
System.out.println(value);
}
}
private long initializeValue() {
return 20;
}
public static void main(String[] args) {
new PrivateFieldInEnclosing().execute();
}
}
我认为有可能value
在Y
中被视为0
,因为无法保证赋值value=initializeValue()
在执行器的线程中可见。我说他需要把value
设置为一个易变字段
他反驳了我,说因为它是一个私有实例字段,在创建线程之前赋值,所以这个值是可见的
我仔细研究了一下,但我无法确定我到底可以用什么作为我陈述的依据。有人能帮我吗?谢谢 不管是不是私人的都无关紧要。与此相关的是: 内存一致性影响:提交内存之前线程中的操作 执行器的可运行对象发生在其执行开始之前, 也许在另一个线程中 从。这意味着无论您在调用
submit
之前做了什么,都可以在runnable中看到。您甚至可以在构造executor之后执行它,在这种特殊情况下,执行线程何时实际启动并不重要,因为submit
方法本身提供了非常强大的保证
这是使java.util.concurrent包非常有用的特性之一。您的朋友是正确的。如果变量初始化在按程序顺序调用
Thread.start
之前,则根据JLS 17.4.5,它发生在Thread.start
之前。线程的开始也发生在线程内的第一个操作之前。因此,它也发生在调用dostufwithvalue
之前
由于使用了Executor
:您不知道它何时调用它使用的线程的Thread.start
,因此JLS无法单独涵盖此特定情况。但是从中可以看出,调用submit
可以提供与线程相同的保证。start
:在向执行器提交Runnable之前,线程中的操作发生在其执行开始之前
因为before是可传递的,所以变量初始化发生在
dostufwithvalue
之前。不过,变量作为私有实例字段的部分是不相关的。在这种情况下,线程实际启动的时间甚至不重要,因为submit
保证在任何情况下都会发生。因此,即使您的executor实现使用某种在JVM启动时预先启动的疯狂线程池,只要executor实现遵循API规范,您仍然是安全的。这是真的,但我选择为问题中提出的论点辩护——也就是说,从第一原则,忽略执行人提供更多担保的事实。你的回答很好地解释了executor类的观点。@Joni我不确定。如果运行execute()。因此,在本案中需要@SergeyTachenov所指出的遗嘱执行人担保之前,会发生这种情况。“不是吗?”凯达姆哈斯韦德,在我给出答案之前,链条的哪一环发生了,你怀疑吗?我可能是wrong@Joni,从您的回答和上面的评论中可以看出,尽管使用了executor.submit(new Runnable(){}),但是会保留before边缘代码>我不确定我们是否可以肯定地说:-)。