1 緩存導致的可見性問題 一個線程對共用變數的修改,另一個線程可以立即看到,這稱之為可見性。 Java記憶體模型規定所有的變數存儲在主記憶體中。每個線程都有自己的工作記憶體,線程在工作記憶體中保存了使用到的主記憶體中變數的副本拷貝,線程對變數的操作必須在工作記憶體中進行,不能直接讀寫主記憶體中的變數。不同線程之間 ...
1 緩存導致的可見性問題
一個線程對共用變數的修改,另一個線程可以立即看到,這稱之為可見性。
Java記憶體模型規定所有的變數存儲在主記憶體中。每個線程都有自己的工作記憶體,線程在工作記憶體中保存了使用到的主記憶體中變數的副本拷貝,線程對變數的操作必須在工作記憶體中進行,不能直接讀寫主記憶體中的變數。不同線程之間無法訪問對方工作記憶體的變數。線程之間共用變數值的傳遞均需要通過主記憶體來完成。
當線程1對共用變數A進行修改之後,線程2的工作記憶體中A可能還不是最新的值。這時候線程1的操作對線程2就不具有可見性。
2 線程切換帶來的原子性問題
我們把一個或者多個操作在CPU執行期間不被打斷的特性成為原子性。
Java中的一條語句,在翻譯為機器碼之後,可能對應的是多個指令。
比如:i++這個操作至少需要3條指令;
- 把 i 的值從記憶體=載入到寄存器;
- 執行+1操作;
- 把值寫入記憶體;
假如 i=0,兩個線程同時執行該操作,可能線程1執行完第一步,就切換到線程2執行,本來兩個線程各執行一次後 i 的值應該為 2 ,此時就出現 兩次遞增操作後值為 1 的現象;
3 編譯優化帶來的有序性問題
Java程式中,如果在本線程中觀察,所有的操作都是有序的;如果在另一個線程觀察,所有的操作都是無序的。前半句指的是線程內表現為串列的語義,後半句指的是指令重排序和主記憶體和工作記憶體同步延遲的問題。
為了充分利用處理器的性能,處理器會對輸入的代碼進行亂序執行。在計算之後將亂序執行的結果重組,並保證該結果和順序執行的結果一致,但是並不保證程式中各個語句的計算順序和輸入代碼的順序一致。Java虛擬機也有類似的指令重排序優化。
比如:Object obj = new Object(),
這條語句對應的指令為:
- 分配一塊記憶體M;
- 在M上初始化 Object 對象;
- 將M的地址賦值給 obj;
電腦經過優化後可能先執行第三步,再第二步,如果執行完第三步後切換到別的線程,若此時訪問該變數則會發生空指針異常;