2

现在在 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。

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

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

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

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

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

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 为空?

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

4

1 回答 1

5

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

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

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

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

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

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

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

于 2017-12-30T08:02:48.697 回答