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

时间:2017-12-30 07:30:59

标签: java multithreading concurrency visibility happens-before

现在已经有很好的答案已经在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,都不会有所不同。所以这可以重新排序,但是由于在保证之前发生了,这是不可能的吗? / p>

java规范没有说明这一点的强烈声明。相反,它说

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

然而,k = 8并不是在与该陈述的关系之前发生的......

我甚至不确定他们的意思是在与start方法的关系之前发生了什么事情,除非您使用相同的监视器锁定例如

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();

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

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

1 个答案:

答案 0 :(得分:4)

  

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

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

  

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

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

  

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

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

总结 - 是一个线程(T1)启动另一个线程(T2),然后在开始T2之前由T1做出的任何更改都保证对T2可见。作为程序员,这才是最重要的。只要不破坏此保证,编译器就可以执行重新排序。你当然可以参考JLS,但我认为你已经有了。