Java 内存模型
為了更好的理解 Java 是如何實現(xiàn) 按需禁用緩存和編譯優(yōu)化 的,我們首先需要對 Java 的內存模型有一個初步的了解。
Java 內存模型主要由以下三部分構成:1 個主內存、n 個線程、n 個工作內存(與線程一一對應),數(shù)據(jù)就在它們三者之間來回倒騰。那么怎么倒騰呢?靠的是 Java 提供給我們的 8 個原子操作:lock、unlock、read、load、use、assign、store、write,其操作流程示意圖如下:
一個變量從主內存拷貝到工作內存,再從工作內存同步回主內存的流程為:
|主內存| -> read -> load -> |工作內存| -> use -> |Java線程| -> assign -> |工作內存| -> store -> write -> |主內存|
Java 內存模型中的 8 個原子操作
- lock:作用于主內存,把一個變量標識為一個線程獨占狀態(tài)。
- read:作用于主內存,把一個變量的值從主內存?zhèn)鬏數(shù)骄€程工作內存中,供之后的 load 操作使用。
- load:作用于工作內存,把 read 操作從主內存中得到的變量值放入工作內存的變量副本中。
- use:作用于工作內存,把工作內存中的一個變量傳遞給執(zhí)行引擎,虛擬機遇到使用變量值的字節(jié)碼指令時會執(zhí)行。
- assign:作用于工作內存,把一個從執(zhí)行引擎得到的值賦給工作內存的變量,虛擬機遇到給變量賦值的字節(jié)碼指令時會執(zhí)行。
- store:作用于工作內存,把工作內存中的一個變量傳送到主內存中,供之后的 write 操作使用。
- write:作用于主內存,把 store 操作從工作內存中得到的變量值存入主內存的變量中。
- unlock:作用于主內存,釋放一個處于鎖定狀態(tài)的變量。
8 個原子操作的執(zhí)行規(guī)則
有關變量拷貝過程的規(guī)則
- 不允許 read 和 load,store 和 write 單獨出現(xiàn)
- 不允許線程丟棄它最近的 assign 操作,即工作內存變化之后必須把該變化同步回主內存中
- 不允許一個線程在沒有 assign 的情況下將工作內存同步回主內存中,也就是說,只有虛擬機遇到變量賦值的字節(jié)碼時才會將工作內存同步回主內存
- 新的變量只能從主內存中誕生,即不能在工作內存中使用未被 load 和 assign 的變量,一個變量在 use 和 store 前一定先經過了 load 和 assign
有關加鎖的規(guī)則
- 一個變量在同一時刻只允許一個線程對其進行 lock 操作,但是可以被一個線程多次 lock(鎖的可重入)
- 對一個變量進行 lock 操作會清空這個變量在工作內存中的值,然后在執(zhí)行引擎使用這個變量時,需要通過 assign 或 load 重新對這個變量進行初始化
- 對一個變量執(zhí)行 unlock 前,必須將該變量同步回主內存中,即執(zhí)行 store 和 write 操作
- 一個變量沒有被 lock,就不能被 unlock,也不能去 unlock一個被其他線程 lock 的變量
可見性問題 -> 有序性問題
通過上圖可以發(fā)現(xiàn),Java 線程只能操作自己的工作內存,其對變量的所有操作(讀取、賦值等)都必須在工作內存中進行,不能直接讀寫主內存中的變量。這就有可能會導致可見性問題:
- 因為對于主內存中的變量 A,其在不同的線程的工作內存中可能存在不同的副本 A1、A2、A3。
- 不同線程的 read 和 load、store 和 write 不一定是連續(xù)執(zhí)行的,中間可以插入其他命令。Java 只能保證 read 和 load、store 和 write 的執(zhí)行對于一個線程而言是連續(xù)的,但是并不保證不同線程的 read 和 load、store 和 write 的執(zhí)行是連續(xù)的,如下圖:
假設有兩個線程 A 和 B,其中線程 A 在寫入共享變量,線程 B 要讀取共享變量,我們想讓線程 A 先完成寫入,線程 B 再完成讀取。此時即便我們是按照 “線程 A 寫入 -> 線程 B 讀取” 的順序開始執(zhí)行的,真實的執(zhí)行順序也可能是這樣的:storeA -> readB -> writeA -> loadB,這將導致線程 B 讀取的是變量的舊值,而非線程 A 修改過的新值。也就是說,線程 A 修改變量的執(zhí)行先于線程 B 操作了,但這個操作對于線程 B 而言依舊是不可見的。
那么如何解決這個問題呢?通過上述的分析可以發(fā)現(xiàn),可見性問題的本身,也是由于不同線程之間的執(zhí)行順序得不到保證導致的,因此我們也可以將它的解決和有序性合并,即對 Java 一些指令的操作順序進行限制,這樣既保證了有序性,有解決了可見性。
于是乎,Java 給出了一些命令執(zhí)行的順序規(guī)范,也就是大名鼎鼎 Happens-Before 規(guī)則。
總結
- 上一篇: 好久没发胡说八道的贴了,今天发一贴
- 下一篇: java美元兑换,(Java实现) 美元