線程與記憶體互動操作,線程記憶體操作
線程與記憶體互動操作
所有的變數(執行個體欄位,靜態欄位,構成數組對象的 元素,不包括局部變數和方法參數)都儲存在主記憶體中,每個線程有自己的工作記憶體,線程的工作記憶體儲存被線程使用到變數的主記憶體副本拷貝。線程對變數的所有操作都必須在工作記憶體中進行,而不能直接讀寫主記憶體的變數。不同線程之間也不能直接存取對方工作記憶體中的變數,線程間變數值的傳遞通過主記憶體來完成。
Java記憶體模型定義了八種操作
- lock(鎖定):作用於主記憶體的變數,它把一個變數標識為一個線程獨佔的狀態
- unlock(解鎖):作用於主記憶體的變數,它把一個處於鎖定狀態的變數釋放出來,釋放後的變數才可以被其他線程鎖定
- read(讀取):作用於主記憶體的變數,它把一個變數的值從主記憶體傳送到線程中的工作記憶體,以便隨後的load動作使用
- load(載入):作用於工作記憶體的變數,它把read操作從主記憶體中得到的變數值放入工作記憶體的變數副本中
- use(使用):作用於工作記憶體的變數,它把工作記憶體中一個變數的值傳遞給執行引擎
- assign(賦值):作用於工作記憶體的變數,它把一個從執行引擎接收到的值賦值給工作記憶體中的變數
- store(儲存):作用於工作記憶體的變數,它把工作記憶體中的一個變數的值傳送到主記憶體中,以便隨後的write操作
- write(寫入):作用於主記憶體的變數,它把store操作從工作記憶體中得到的變數的值寫入主記憶體的變數中
Java記憶體模型還規定了執行上述8種基本操作時必須滿足如下規則
1、不允許read和load、store和write操作之一單獨出現,以上兩個操作必須按順序執行,但沒有保證必須連續執行,也就是說,read與load之間、store與write之間是可插入其他指令的。
2、不允許一個線程丟棄它的最近的assign操作,即變數在工作記憶體中改變了之後必須把該變化同步回主記憶體。
3、不允許一個線程無原因地(沒有發生過任何assign操作)把資料從線程的工作記憶體同步回主記憶體中。
4、一個新的變數只能從主記憶體中“誕生”,不允許在工作記憶體中直接使用一個未被初始化(load或assign)的變數,換句話說就是對一個變數實施use和store操作之前,必須先執行過了assign和load操作。
5、一個變數在同一個時刻只允許一條線程對其執行lock操作,但lock操作可以被同一個條線程重複執行多次,多次執行lock後,只有執行相同次數的unlock操作,變數才會被解鎖。
6、如果對一個變數執行lock操作,將會清空工作記憶體中此變數的值,在執行引擎使用這個變數前,需要重新執行load或assign操作初始設定變數的值。
7、如果一個變數實現沒有被lock伺服器用戶端檔案鎖,則不允許對它執行unlock操作,也不允許去unlock一個被其他線程鎖定的變數。
8、對一個變數執行unlock操作之前,必須先把此變數同步回主記憶體(執行store和write操作)。
volatile關鍵字的作用
保證了新值能立即儲存到主記憶體,每次使用前立即從主記憶體中重新整理。
禁止指令重排序最佳化。
註:volatile關鍵字不能保證在多線程環境下對共用資料的操作的正確性。可以使用在自己狀態改變之後需要立即通知所有線程的情況下。
final域
final類型的域是不能修改的,除了這一點外,在Java記憶體模型中,final域還有著特殊的語義,final域能確保初始化過程的安全性,從而可以不受限制地訪問不可變對象,並在共用這些對象時無須同步。具體而言,就是被final修飾的欄位在構造器中一旦被初始化完成,並且構造器沒有把“this”的引用傳遞出去(this引用逃逸是一件很危險的事情,其他線程有可能通過這個引用訪問到“初始化了一半”的對象),那麼在其他線程中就能看到final欄位的值,而且其外、外部可見狀態永遠也不會改變。它所帶來的安全性是最簡單最純粹的。