(一)java內存區域概況
jvm運行java程序時把所管理的內存分成幾個部分:方法區、java棧、本地方法棧、java堆、pc程序計數器。
class字節碼裝載解析後,在多線程環境中,方法區和java堆數據共享,每個線程自帶pc程序計數器和java棧,棧幀中包含方法的所有狀態(局部變量、傳參、返回值、運算中間結果等)。對共享數據需要考慮多線程併發問題。
更詳細內容可參考《深入理解JVM虛擬機》。
(二)計算機系統原型
1.內存和處理器速度不同,緩存的出現,導致數據不同步
多線程都有緩存備份,回寫可能會導致數據錯誤
2.處理器和編譯器對代碼和指令的優化,導致運算亂序
只要不影響運算結果,處理器可以調整代碼執行順序,多線程操作中會導致數據錯誤
(三)JMM概況
JMM(java momery model)提供了線程進行通訊時,確保互斥性和可見性。
(1)解決內存可見性問題
共享數據放主內存中,每個線程有自己的工作內存。
線程A讀取數據時,先從主內存拷貝數據到工作內存,進行運算後再從工作內存回寫到主內存,線程間無法相互訪問對方的數據,通過主內存實現通訊。
JMM可以控制指定共享數據實現內存可見性,寫入主內存時,同步更新其他線程中的變量值,如volatile變量的使用。
(2)禁止重排序
編譯器優化代碼,不改變運算結果的情況下,可以實現代碼重排序;處理器執行指令,不改變運算結果的情況下,可以實現指令重排序。
JMM可以禁止編譯器代碼重排序和處理器執行指令重排序(通過加入內層屏蔽)。
多線程併發問題代碼剖析
/**
* 代碼重排序demo
*
* @author peter_wang
* @create-time 2015-1-8 下午3:06:02
*/
public class CodeChangeDemo {
private static boolean isChange = false;
private static class ChangeThread
extends Thread {
public void run() {
System.out.println("ChangeThread start"); //1
isChange = true; //2
};
};
private static class GetChangeThread
extends Thread {
public void run() {
System.out.println("GetChangeThread start");
if (isChange) { //3
isChange = false;
System.out.println("change success");
}
else {
System.out.println("change no success");
System.exit(0);
}
};
};
/**
* @param args
*/
public static void main(String[] args) {
while (true) {
ChangeThread changeThread = new ChangeThread();
GetChangeThread getChangeThread = new GetChangeThread();
changeThread.start();
getChangeThread.start();
// 防止線程過多,等執行完上面倆線程再繼續
while (Thread.activeCount() > 1) {
}
}
}
}
運行結果:
ChangeThread start
GetChangeThread start
change success
ChangeThread start
GetChangeThread start
change success
ChangeThread start
GetChangeThread start
change no success
進程退出可能原因:
1.線程ChangeThread中代碼順序調整,1和2調換,執行順序1-3-2
2.線程ChangeThread執行完,工作內存中的isChange沒有回寫回主內存,導致3中isChange不是最新值。
參考文獻:
2.http://ifeve.com/java-memory-model-1/