2017-09-06 40 views
3

我现在正在阅读“有效Java”并遇到混淆。项目66:同步对共享可变数据的访问

对于代码1(java8):

public class StopThreadTest { 

    private static Boolean stopRequest = false; 

    public static void main(String[] args) throws InterruptedException { 
     new Thread(()->{ 
      int i = 0; 
      while (!stopRequest) { 
       i++; 
       //System.out.println("i: " + i); 
      } 
     }).start(); 

     TimeUnit.SECONDS.sleep(1); 
     stopRequest = true; 
    } 
} 

程序永远不会终止。

对于代码2(java8):

public class StopThreadTest { 

    private static Boolean stopRequest = false; 

    public static void main(String[] args) throws InterruptedException { 
     new Thread(()->{ 
      int i = 0; 
      while (!stopRequest) { 
       i++; 
       System.out.println("i: " + i); 
      } 
     }).start(); 

     TimeUnit.SECONDS.sleep(1); 
     stopRequest = true; 
    } 
} 

只是增加System.out.println(),该程序运行约1秒。

有人可以告诉我为什么吗?

+0

这是有效的Java的官方例子吗?如果是这样:哪个版本? – Turing85

+0

@ Turing85当然不在我的书中。实际上,这本书与这个例子相矛盾,说读写'stopRequest'必须同步。不仅仅是一些随机的方法,它的内部实现恰好是同步的。 –

+0

在第二版中,作者使用代码1来解决必须同步读取和写入stopRequest的问题。我只是添加一个System.out.println来调试它,就像代码2一样。 – Dustin

回答

3

System.out.println()已同步,消除了原始代码的可见性问题。没有它,该线程可以使用其缓存的值stopRequest并继续运行,但是当涉及println()时,刷新缓存并且可以看到修改后的值。

PrintStream.println(String x)

synchronized (this) { 
    print(x); 
    newLine(); 
} 

请注意,这只是一个副作用。它解释了行为的差异,但它不是代码的正确功能所依赖的。

+2

由于从Kayaman解释的原因,如果您将stopRequest定义为volatile,那么您可以避免缓存问题,并且这两种代码的行为都是相同的:一秒后程序将停止。 –

+2

我不太确定这是否正确。虽然'System.out.println(...)'是'synchronized',我看不出任何理由,任何读或写'stopRequest'都应该是'synchronized'。你能否提供一些JLS引用来支持你的陈述? – Turing85

+1

@ KDavid-Valerio这意味着代码仅仅是因为println()的副作用''!!我也会对规范的一些链接感兴趣。 – blafasel