2.什么是JAVA内存模型?
為什么要設計JAVA內存模型?
小陳:老王,看了上一篇的《CPU多級緩存模型》,有個疑問為什么還要有JAVA內存模型啊?
老王:這么來說吧,CPU多級緩存模型,只是一個規范,但是底層基于這個規范的實現還是有很多種的。比如:
(1)基于這個模型之上不同的計算機廠商可能對這個模型底層的實現不一樣,包括底層運行的指令集、有的還基于多級緩存模型之上還引入了寫換從器、無效隊列等。
(2)不同的操作系統windows、linux的指令集不一樣。
老王:
所以JAVA語言為了應對上述的問題:不同的系統、不同的計算機廠商的底層實現不同。基于它們之上設計了一個JAVA內存模型(JMM)的規范,就是為了屏蔽底層操作系統、廠商之間的差異性。程序員運行是只需要關注JMM,而不需關注底層系統、廠商指令的差異性,這些由JMM去適配不同的系統和廠商的指令集,這樣就能實現一次編寫,“到處亂跑”的跨平臺效果。
小陳:哦哦,原來是為了屏蔽操作系統、不同的計算機廠商的底層的差異性,實現跨平臺的無差異性運行的效果啊。
JAVA內存模型是怎么樣的?
小陳:原因我大概知道了,那JAVA內存模型大概是個什么樣的結構啊?
老王:給你看下下面這張圖,你大概就理解了
老王:上圖就是JAVA內存模型的大致結構圖,JAVA內存模型定義了一個規范。那就是每個線程都有自己的工作內存,線程操作共享變量的時候需要從主內存讀取到自己的工作內存,然后在傳遞給工作線程使用,共享變量修改后先刷新到工作內存,然后再刷新回主內存;這個JAVA內存模型是基于我們上一講CPU多級緩存模型上建立的。
JAVA內存模型定義的八種原子操作
小陳:看了上面的圖,JAVA內存模型的結構我基本知道了,但是JMM這哥們怎么工作的我還是很模糊啊?
老王:沒關系,我來給你講講,JMM定義了8種指令,它工作的時候就是通過這8種指令來操作內存的。
包括怎么鎖定變量、怎么將數據從主存傳到工作內存、怎么傳給正在被CPU調度的線程、修改之后CPU怎么傳回工作內存、工作內存又怎么傳遞回主存:
1. lock(鎖定):把主內存中的一個共享變量標記為一個線程獨享的狀態
2. unlock(解鎖):把主內存的變量從線程獨享的lock狀態中解除出來
3. read(讀取):把主內存的一個共享變量傳輸工作內存中
4. load(載入):把從主內存傳輸到工作內存的共享變量,賦值給工作內存中的變量副本
5. use(使用):把工作內存中的變量副本的值,傳遞給執行引擎CPU
6. assign(賦值):執行引擎(CPU)執行完之后,把修改過的變量值重新賦值給工作內存中的變量副本
7. store(存儲):把工作內存中修改過共享變量的值傳遞到主內存中
8. write(寫入):把傳遞到主內存中的變量值,重新寫回給主內存的共享變量
小陳:...., 這都啥啊...
老王:給你畫張圖吧,你就了解了:
比如線程A要執行一個x++的操作,可能要經歷下面的過程:
小陳:牛逼啊老王....
小陳:額,感覺不對啊,上面只有六種指令,還有另外兩種lock、unlock指令是咋用的?
老王:這還不簡單,再給你畫張圖就KO了。
比如還是上面的x++操作
(1)工作線程A操作該共享內存變量的時候,執行lock指令,主內存中的這個共享變量;同時告訴線程B這個共享變量我準備修改了,讓它失效掉。
(2)B線程的變量副本失效之后,運行時候用到,需要到主內存重新讀取(執行read、load操作放入工作內存);發現該主內存的變量被鎖定了,讀取失敗;此時相當于線程A擁有該變量的獨享操作
(3)線程A執行i++操作,經歷上述說的(read、load、use、assign、store、write)指令之后;操作完成執行unlock釋放鎖定的這個內存變量
(4)線程B這個時候再去主內存讀取的時候,發現未被鎖定,就可以重新讀取了
小陳:牛逼啊老王,聽君一席話,白讀十年書。獻上我的掌聲......
多線程并發在JMM操作帶來的可見性問題
小陳:JMM以及它的8中基本操作我了解了,但是我發現了個問題,在多線程操作的時候可能會有數據不一致的問題,比如我畫個圖講一下我的想法:
線程A和線程B同時執行共享變量x的++操作;線程B在線程A將x=1的值刷入主內存之前讀取x=0,這樣就會導致數據錯了。
老王:很好,看來小陳很聰明啊,理解得很快啊
小陳:嘿嘿,一般般啦...
老王:你說的這個問題,正是多線程并發操作的可見性問題。
小陳:我知道多線程并發操作的時候,會有可見性、有序性、原子性的問題,這種情況會導致可見性問題我知道了,但是有序性和原子性是什么?什么時候會發生的?會導致什么問題?
老王:哈哈,你有這個好奇心很好,我們今天先說這么多,可見性、有序性、原子性的問題我們留到下一篇再說,別急哦,一點點慢慢來,嘖嘖..
關注小陳,公眾號上更新更多的java并發文章
JAVA并發文章目錄(公眾號)
JAVA并發專題 《筑基篇》
1.什么是CPU多級緩存模型?
2.什么是JAVA內存模型?
3.線程安全之可見性、有序性、原子性是什么?
4.什么是MESI緩存一致性協議?怎么解決并發的可見性問題?
JAVA并發專題《練氣篇》
5.volatile怎么保證可見性?
6.什么是內存屏障?具有什么作用?
7.volatile怎么通過內存屏障保證可見性和有序性?
8.volatile為啥不能保證原子性?
9.synchronized是個啥東西?應該怎么使用?
10.synchronized底層之monitor、對象頭、Mark Word?
11.synchronized底層是怎么通過monitor進行加鎖的?
12.synchronized的鎖重入、鎖消除、鎖升級原理?無鎖、偏向鎖、輕量級鎖、自旋、重量級鎖
13.synchronized怎么保證可見性、有序性、原子性?
JAVA并發專題《結丹篇》
14. JDK底層Unsafe類是個啥東西?
15.unsafe類的CAS是怎么保證原子性的?
16.Atomic原子類體系講解
17.AtomicInteger、AtomicBoolean的底層原理
18.AtomicReference、AtomicStampReference底層原理
19.Atomic中的LongAdder底層原理之分段鎖機制
20.Atmoic系列Strimped64分段鎖底層實現源碼剖析
JAVA并發專題《金丹篇》
21.AQS是個啥?為啥說它是JAVA并發工具基礎框架?
22.基于AQS的互斥鎖底層源碼深度剖析
23.基于AQS的共享鎖底層源碼深度剖析
24.ReentrantLock是怎么基于AQS實現獨占鎖的?
25.ReentrantLock的Condition機制底層源碼剖析
26.CountDownLatch 門栓底層源碼和實現機制深度剖析
27.CyclicBarrier 柵欄底層源碼和實現機制深度剖析
28.Semaphore 信號量底層源碼和實現機深度剖析
29.ReentrantReadWriteLock 讀寫鎖怎么表示?
30. ReentrantReadWriteLock 讀寫鎖底層源碼和機制深度剖析
JAVA并發專題《元神篇》并發數據結構篇
31.CopyOnAarrayList 底層分析,怎么通過寫時復制副本,提升并發性能?
32.ConcurrentLinkedQueue 底層分析,CAS 無鎖化操作提升并發性能?
33.ConcurrentHashMap詳解,底層怎么通過分段鎖提升并發性能?
34.LinkedBlockedQueue 阻塞隊列怎么通過ReentrantLock和Condition實現?
35.ArrayBlockedQueued 阻塞隊列實現思路竟然和LinkedBlockedQueue一樣?
36.DelayQueue 底層源碼剖析,延時隊列怎么實現?
37.SynchronousQueue底層原理解析
JAVA并發專題《飛升篇》線程池底層深度剖析
38. 什么是線程池?看看JDK提供了哪些默認的線程池?底層竟然都是基于ThreadPoolExecutor的?
39.ThreadPoolExecutor 構造函數有哪些參數?這些參數分別表示什么意思?
40.內部有哪些變量,怎么表示線程池狀態和線程數,看看道格.李大神是怎么設計的?
41. ThreadPoolExecutor execute執行流程?怎么進行任務提交的?addWorker方法干了啥?什么是workder?
42. ThreadPoolExecutor execute執行流程?何時將任務提交到阻塞隊列? 阻塞隊列滿會發生什么?
43. ThreadPoolExecutor 中的Worker是如何執行提交到線程池的任務的?多余Worker怎么在超出空閑時間后被干掉的?
44. ThreadPoolExecutor shutdown、shutdownNow內部核心流程
45. 再回頭看看為啥不推薦Executors提供幾種線程池?
46. ThreadPoolExecutor線程池篇總結
總結
以上是生活随笔為你收集整理的2.什么是JAVA内存模型?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何对NTFS文件进行压缩和加密
- 下一篇: 人工智能机器视觉专业英语积累