2016-10-17 56 views
3

該程序的答案必須在5秒後「更改完成」,但我得到「更改完成」和「完成」。我沒有getDone方法作爲同步。任何想法我正在爲線程完成處理做什麼。java synchronized同步主題說明

public class Main { 
    private static boolean done = false; 
    private static int count; 

    public static void main(String[] args)throws InterruptedException { 
     new Thread(() -> { 
      while (!getDone()) { 
       count = count + 1; 
      } 
      System.out.println("DONE!!!!"); 
     }).start(); 
      Thread.sleep(5000); 
     System.out.println("Changing done"); 
     synchronized (Main.class) { 
      done = true; 
     } 
    } 

    public static boolean getDone() { 
     return done; 
    } 
} 
+2

您的程序正在執行我期望的操作。你是否期望你的匿名'Runnable'沒有完成? – CodeBlind

+0

@CodeBlind:對'done'的訪問沒有正確同步,對'getDone()'的調用可能看不到當前值。 – user140547

+1

這裏可能發生的最糟糕的情況是工作線程可能無法立即看到'done'中的變化,但它最終肯定會發生。由於只有一個線程改變了'done',所以你可以在聲明中使用'volatile'關鍵字並完全拋棄'synchronized'塊。但這是橋下的一切 - 我仍然不明白OP的目標。他似乎感到驚訝的是,工作者線程正在打印「完成!!!!」。 – CodeBlind

回答

1

我沒有getDone方法作爲同步。任何想法我正在爲線程完成處理做什麼。

正如你所提到的,done之間沒有明確的內存同步,就像你的自旋線程和主線程一樣。雖然主線程在退出​​塊時跨越了寫入內存屏障,但旋轉線程並沒有顯式讀取內存屏障。

但是,線程有很多方法可以查看更新的信息。如果操作系統將線程從正在運行的處理器中切換出來,則緩存的內存可能會丟失,因此當線程被換回時,它將向中央內存請求done,並將看到更新。

而且,雖然你的示例代碼不顯示它,如果您對其他​​方法的調用(如System.out.println())或其他交叉內存屏障(訪問另一個volatile場),那麼這將也會導致done進行更新。

5

如果不同步訪問done得當,就意味着你的代碼可能失敗,即線程可能不會看到更新後的值。

這並不意味着值保證不可見,除非正確同步。因此,在很多情況下,寫入done仍然可見(事實上,破碎的代碼在很多情況下仍然有效,使得併發編程變得更加困難)。在任何情況下都不能保證工作。

+0

完成更改是在同步塊中執行的。它將可見。這在內存模型下的[JLS](https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4)中有說明。你有具體的參考證明爲什麼線程可能看不到更新的值?或者,也許它在我已包含的參考文獻中,但在粗略閱讀之後,我認爲所做的更改將會顯現。 – matt

+0

@matt:不僅寫入必須同步,還要讀取。你不需要JLS。看到例如,從接受的答案(重點礦)http://stackoverflow.com/questions/9196723/learning-java-use-of-synchronized-keyword引用:當一個同步方法退出時,它會自動建立一個發生之前與**任何後續調用同一對象的同步方法的關係** – user140547

+0

我不是說那裏不會是競爭條件。同步保證更改將在線程間可見。它類似於聲明變量volatile。 – matt