2015-11-06 51 views
1

我一直在試驗Java線程可見性問題,通過一個共享的布爾和非易失性變量向目標線程發送一個停止信號到線程的流行示例,目標線程似乎沒有得到它)如下圖所示:Java線程可視性和同步

public class ThreadVisibilityTest { 

    //Shared variable to send a signal to the thread 
    static boolean stopped = false; 

    public static void main(String[] args) throws Exception { 

     Thread targetThread = new Thread(new Runnable() { 
       public void run() { 
       while(!stopped) {} 
       System.out.println("Target thread gets signal and stops..."); 
       } 
       }); 

     targetThread.start(); 

     TimeUnit.SECONDS.sleep(5); 
     stopped=true; 
     System.out.println("Main thread has sent stop signal to the thread..."); 

    } 

} 

主線程設置stopped爲true,目標線程無法得到它的手段在5秒後發出停止信號給目標線程,因此不會停止。

定義stopped變量爲volatile明顯解決了這個問題。

卜後來我意識到,如果我做stopped變量non volatile而是訪問它在目標線程​​背景,目標線程得到最終值和停止。所以線程可見性問題似乎可以像使用volatile來解決。

Thread targetThread = new Thread(new Runnable() { 
      public void run() { 
       while(true) { 
        synchronized(this) { 
         if(stopped) break; 
        } 
       } 
       System.out.println("Target thread gets signal and stops..."); 
      } 
     }); 

而且還可以用於同步的對象監視器似乎沒有任何效果如下:

synchronized(Thread.class) { 
     if(stopped) break; 
} 

這東西是偶然發生還是我錯過了什麼? 或者我們可以說通過互斥訪問共享變量似乎強制目標線程刷新其緩存內存,就像訪問變量volatile一樣嗎?

如果後者是真實的你建議克服線程可見性問題,通過揮發性關鍵字或訪問互斥?

在此先感謝

+0

如果您知道必須使用此變量的最新值,爲什麼您會選擇不使用易失性?你認爲它有什麼問題? –

+0

如果你想使用同步,你當然必須做在相同的監視器上同步停止塊的寫入和讀取。您不會在同步塊中寫入該值。 –

+0

我不是不想選擇易變的。我試圖理解爲什麼互斥也解決了內存可見性問題,因爲我已經測試並發現,通過互斥來訪問共享變量會使您像volatile一樣獲得最終值。 – Serdar

回答

0

我覺得互斥還提供了部分3.1.3鎖定和能見度在Java併發在實踐聲明(Brian Goetz撰寫)內存的知名度。

2

這是偶然發生的事情還是我錯過了什麼?

您錯過了討論Java內存模型的Java語言參考(JLS)一章。要麼是這樣,要麼你錯過了Java教程中併發性一章的工作。 https://docs.oracle.com/javase/tutorial/essential/concurrency/

無論哪種方式,你會了解到,如果從一個synchronized塊線程A退出,然後線程B進入是同一對象上同步塊,那麼線程A一切釋放鎖是以前寫保證在線程B鎖定鎖之後對線程B可見。

0

看到,您正在同步的上下文中閱讀它,但未在同步上下文中編寫。這可能會導致問題。