在 thread.start 之前发生的所有事情对调用 start 的线程可见吗?



现在在stackoverflow上已经有很好的答案了,但它们没有给我想要的明确答案。

假设你有一种方法

Dosomething();
doAnother();
int x = 5;
Runnable r = new Runnable(){
public void run(){
int y = x;
x = 7;
System.out.println(z);}
}
new Thread(r).start()

现在,此方法正在运行,在调用thread.start之前,一些全局非易失性变量z从4更改为5。

程序是否保证打印 5,因为 z 发生在 thread.start 之前?

另外,如果我们以某种方式谈论它,那么可以肯定地说thread.start()永远不能重新排序。

这意味着就被调用的线程而言,就好像到目前为止的所有内容都是顺序的。例如,假设我们有

int k = 8;
new Thread(() -> {}).start()

现在。。。无论首先调用 start 还是分配 k 8,该线程的视角都不会产生任何影响。所以这可以重新排序,但由于在保证之前发生,这是不可能的吗?

java 规范并没有说一个强有力的声明来说明这一点。相反,它说

当一个语句调用 Thread.start() 时,与该语句有发生前关系的每个语句

然而 k = 8 并不是说在与该语句的关系之前发生......

我什至不确定它们的意思是在与启动方法的关系之前发生了某些事情,除非您使用相同的显示器锁定,例如

synchronized(this){ int k = 8;}
synchronized(this) { new Thread(() -> {}).start();}

对于更可怕的情况,我们有这个代码

Socket con = socket.accept();
Runnable r = new Runnable(){ 
public void run(){
handleRequest(con)}
}
new Thread(r).start();

然后新线程碰巧发现该骗局为空?

有人可以就这些主题给我一个明确的答案吗?

程序是否保证打印 5,因为 z 发生在 thread.start 之前?

如果 z 的值是由调用 start() 方法的线程设置的,那么是的。否则,没有。新启动的线程保证看到启动它的线程所做的更改,而不是其他线程所做的更改。

另外,如果我们以某种方式谈论它,那么可以肯定地说thread.start()永远不能重新排序。

关键是 - 新启动的线程保证将 k 的值视为 8。如果新启动的线程不读取k(或父线程设置的任何其他变量),则允许编译器对此类操作重新排序,但对程序员来说无关紧要(无论如何,程序员都会得到保证)

然后新线程碰巧发现该骗局为空?

假设新线程引用了 con,则保证在新线程启动之前初始化 con(因为 JMM 保证父线程在调用 start() 之前所做的任何更改对新线程都是可见的)

总而言之 - 是一个线程 (T1) 启动另一个线程 (T2),那么 T1 在启动 T2 之前所做的任何更改都保证对 T2 可见。作为一名程序员,这才是最重要的。允许编译器执行重新排序,只要它们不违反此保证。你当然可以参考JLS,但我认为你已经有了。

最新更新