JVM并发机制探讨—内存模型、内存可见性和指令重排序
并發本來就是個有意思的問題,尤其是現在又流行這么一句話:“高帥富加機器,窮矮搓搞優化”。從這句話可以看到,無論是高帥富還是窮矮搓都需要深入理解并發編程,高帥富加多了機器,需要協調多臺機器或者多個CPU對共享資源的訪問,因此需要了解并發,窮矮搓搞優化需要編寫各種多線程的代碼來壓榨 CPU的計算資源,讓它在同一時刻做更多的事情,這個更需要了解并發。
并發本來就是個有意思的問題,尤其是現在又流行這么一句話:“高帥富加機器,窮矮搓搞優化”。從這句話可以看到,無論是高帥富還是窮矮搓都需要深入理解并發編程,高帥富加多了機器,需要協調多臺機器或者多個CPU對共享資源的訪問,因此需要了解并發,窮矮搓搞優化需要編寫各種多線程的代碼來壓榨 CPU的計算資源,讓它在同一時刻做更多的事情,這個更需要了解并發。
在我前一篇關于并發的文章http://my.oschina.net/chihz/blog/54731中提到過管程,管程的特色是在編程語言中 對并發的細節進行封裝,使程序員可以直接在語言中就得到并發的支持,而不必自己去處理一些像是控制信號量之類容易出錯且繁瑣的細節問題。一些語言是通過在 編譯時解開語法糖的方式去實現管程,但Java在編譯后生成的字節碼層面上對并發仍然是一層封裝,比如syncrhonized塊在編譯之后只是對應了兩 條指令:monitorenter和monitorexit。更多的并發細節是在JVM運行時去處理的,而不是編譯。這篇文章主要是針對JVM處理并發的 一些細節的探討。
JAVA內存模型
對于我們平時開發的業務應用來說,內存應該是訪問速度最快的存儲設備,對于頻繁訪問的數據,我們總是習慣把它們放到內存緩存中,有句話不是說么,緩 存就像是清涼油,哪里有問題就抹一抹。但是CPU的運算速度比起內存的訪問速度還要快幾個量級,為了平衡這個差距,于是就專門為CPU引入了高速緩存,頻 繁使用的數據放到高速緩存當中,CPU在使用這些數據進行運算的時候就不必再去訪問內存。但是在多CPU時代卻有一個問題,每個CPU都擁有自己的高速緩 存,內存又是所有CPU共享的公共資源,于是內存此時就成了一個臨界區,如果控制不好各個CPU對內存的并發訪問,那么就會產生錯誤,出現數據不一致的情 況。為了避免這種情況,需要采取緩存一致性協議來保證,這類協議有很多,各個硬件平臺和操作系統的實現不盡相同。
JVM需要實現跨平臺的支持,它需要有一套自己的同步協議來屏蔽掉各種底層硬件和操作系統的不同,因此就引入了Java內存模型。對于Java來說 開發者并不需要關心任何硬件細節,因此沒有多核CPU和高速緩存的概念,多核CPU和高速緩存在JVM中對應的是Java語言內置的線程和每個線程所擁有 的獨立內存空間,Java內存模型所規范的也就是數據在線程自己的獨立內存空間和JVM共享內存之間同步的問題。下面這兩張圖說明了硬件平臺和JVM內存 模型的相似和差異之處。
Java內存模型規定,對于多個線程共享的變量,存儲在主內存當中,每個線程都有自己獨立的工作內存,線程只能訪問自己的工作內存,不可以訪問其它 線程的工作內存。工作內存中保存了主內存共享變量的副本,線程要操作這些共享變量,只能通過操作工作內存中的副本來實現,操作完畢之后再同步回到主內存當 中。如何保證多個線程操作主內存的數據完整性是一個難題,Java內存模型也規定了工作內存與主內存之間交互的協議,首先是定義了8種原子操作:
(1) lock:將主內存中的變量鎖定,為一個線程所獨占
(2) unclock:將lock加的鎖定解除,此時其它的線程可以有機會訪問此變量
(3) read:將主內存中的變量值讀到工作內存當中
(4) load:將read讀取的值保存到工作內存中的變量副本中。
(5) use:將值傳遞給線程的代碼執行引擎
(6) assign:將執行引擎處理返回的值重新賦值給變量副本
(7) store:將變量副本的值存儲到主內存中。
(8) write:將store存儲的值寫入到主內存的共享變量當中。
我們可以看到,要保證數據的同步,lock和unlock定義了一個線程訪問一次共享內存的界限,有lock操作也必須有unlock操作,另外一 些操作也必須要成對出現才可以,像是read和load、store和write需要成對出現,如果單一指令出現,那么就會造成數據不一致的問題。 Java內存模型也針對這些操作指定了必須滿足的規則:
(1) read和load、store和write必須要成對出現,不允許單一的操作,否則會造成從主內存讀取的值,工作內存不接受或者工作內存發起的寫入操作而主內存無法接受的現象。
(2) 在線程中使用了assign操作改變了變量副本,那么就必須把這個副本通過store-write同步回主內存中。如果線程中沒有發生assign操作,那么也不允許使用store-write同步到主內存。
(3) 在對一個變量實行use和store操作之前,必須實行過load和assign操作。
(4) 變量在同一時刻只允許一個線程對其進行lock,有多少次lock操作,就必須有多少次unlock操作。在lock操作之后會清空此變量在工作內存中原 先的副本,需要再次從主內存read-load新的值。在執行unlock操作前,需要把改變的副本同步回主存。
轉載于:https://blog.51cto.com/ibin1520/1350561
總結
以上是生活随笔為你收集整理的JVM并发机制探讨—内存模型、内存可见性和指令重排序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Windows Management F
- 下一篇: JDK源码笔记-java.util.Ha