問題來源於編碼規范的一個例子
一. 關於server模式下的主存和工作內存
規則40 多線程訪問同一個可變變量,需增加同步機制
說明:根據Java Language Specification中對Java內存模型的定義, JVM中存在一個主內存(Java Heap Memory),Java中所有變量都儲存在主存中,對於所有線程都是共享的。每個線程都有自己的工作內存(Working Memory),工作內存中保存的是主存中某些變量的拷貝,線程對所有變量的操作都是在工作內存中進行,線程之間無法相互直接訪問,變量傳遞均需要通過主存完成。根據上述內存模型的定義,要在多個線程間安全的同步共享數據就必須使用鎖機制,將某線程中更新的數據從其工作內存中刷新至主內存,並確保其他線程從主內存獲取此數據更新后的值再使用。
示例:
不好:下面的代碼中,沒有對可變數據stopRequested的訪問做同步。程序期望在一秒鍾后線程能停止。但在用java 1.6的server模式運行此程序(Java –server StopThread)時,程序陷入死循環,不能結束。
說明:根據Java Language Specification中對Java內存模型的定義, JVM中存在一個主內存(Java Heap Memory),Java中所有變量都儲存在主存中,對於所有線程都是共享的。每個線程都有自己的工作內存(Working Memory),工作內存中保存的是主存中某些變量的拷貝,線程對所有變量的操作都是在工作內存中進行,線程之間無法相互直接訪問,變量傳遞均需要通過主存完成。根據上述內存模型的定義,要在多個線程間安全的同步共享數據就必須使用鎖機制,將某線程中更新的數據從其工作內存中刷新至主內存,並確保其他線程從主內存獲取此數據更新后的值再使用。
示例:
不好:下面的代碼中,沒有對可變數據stopRequested的訪問做同步。程序期望在一秒鍾后線程能停止。但在用java 1.6的server模式運行此程序(Java –server StopThread)時,程序陷入死循環,不能結束。
public class StopThread { private static boolean stopRequested; public static void main(String[] args) throws InterruptedException { Thread backgroundThread = new Thread(new Runnable() { public void run() { int i = 0; while (!stopRequested) { i++; } } }); backgroundThread.start(); TimeUnit.SECONDS.sleep(1); stopRequested = true; } }
這里為什么會陷入死循環,永遠不會停止呢?
參考兩篇文章
http://m.blog.csdn.net/blog/lyy5682077/17588155
http://www.cnblogs.com/trytocatch/archive/2013/01/07/2850002.html
JIT或HotSpot編譯器在server模式和client模式編譯不同,server模式為了使線程運行更快,如果其中一個線程更改了變量boolean flag 的值,那么另外一個線程會看不到,因為另外一個線程為了使得運行更快所以從寄存器或者本地cache中取值,而不是從內存中取值,那么使用volatile后,就告訴不論是什么線程,被volatile修飾的變量都要從內存中取值。《內存柵欄》
java在server模式下,各個線程使用各自的工作內存,一個線程改變了變量的值,另外一個線程並不會從主存中取
上面例子中的問題,變量
stopRequested前加上volatile可以解決:
增加了
synchronized
同步機制后,程序就能正確地在
1
秒后終止。另一個方案是在變量前增加
volatile
關鍵字。
public class StopThread { private static boolean stopRequested; private static synchronized void requestStop() { stopRequested = true; } private static synchronized boolean isStopRequested() { return stopRequested; } public static void main(String[] args) throws InterruptedException { Thread backgroundThread = new Thread(new Runnable() { public void run() { int i = 0; while (!isStopRequested()) { i++; } } }); backgroundThread.start(); TimeUnit.SECONDS.sleep(1); requestStop(); } }
二. static和volatile的區別
參考http://blog.sina.com.cn/s/blog_4e1e357d0101i486.html
1. volatile是告訴編譯器,每次取這個變量的值都需要從主存中取,而不是用自己線程工作內存中的緩存.
2. static 是說這個變量,在主存中所有此類的實例用的是同一份,各個線程創建時需要從主存同一個位置拷貝到自己工作內存中去(而不是拷貝此類不同實例中的這個變量的值),也就是說只能保證線程創建時,變量的值是相同來源的,運行時還是使用各自工作內存中的值,依然會有不同步的問題.
