thread.start 之前发生的所有事情对于调用 start 生成的线程是否可见?

kat*_*ex7 2 java concurrency multithreading visibility happens-before

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

\n\n

说你有方法

\n\n
Dosomething();\n doAnother();\n  int x = 5;\n  Runnable r = new Runnable(){\n     public void run(){\n     int y = x;\n     x = 7;\n     System.out.println(z);}\n   }\n   new Thread(r).start()\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

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

\n\n

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

\n\n

就被称为启动的线程而言,这意味着,就好像到该点为止的所有内容都是顺序的。例如说我们有

\n\n
int k = 8;\nnew Thread(() -> {}).start()\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

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

\n\n

当语句调用\xc2\xa0Thread.start()时,与该语句有happens-before关系的每个语句

\n\n

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

\n\n

我什至不确定他们的意思是什么,除非您使用相同的监视器锁定,否则在与启动方法的关系之前发生了一些事情

\n\n
synchronized(this){ int k = 8;}\n synchronized(this) { new Thread(() -> {}).start();}\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
Socket con = socket.accept();\nRunnable r = new Runnable(){ \n  public void run(){\n      handleRequest(con)}\n }\nnew Thread(r).start();\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后新线程碰巧发现 con 为 null?

\n\n

有人能给我关于这些主题的明确答案吗?

\n

Ash*_*h A 8

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

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

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

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

然后新线程碰巧发现 con 为 null?

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

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