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;
計算機經過優化后可能先執行第三步,再第二步,如果執行完第三步后切換到別的執行緒,若此時訪問該變數則會發生空指標例外;
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/184517.html
標籤:Java
