1.java內存模型(JMM)
Java Memory Model (JAVA 內存模型)描述線程之間如何通過內存(memory)來進行交互。JMM決定一個線程對共享變量的寫入何時對另一個線程可見。從抽象的角度來看,JMM定義了線程和主內存之間的抽象關系:線程之間的共享變量存儲在主內存(main memory)中,每個線程都有一個私有的本地內存(local memory),本地內存中存儲了該線程以讀/寫共享變量的副本。本地內存是JMM的一個抽象概念,并不真實存在。它涵蓋了緩存,寫緩沖區,寄存器以及其他的硬件和編譯器優化。
從上圖來看,線程A與線程B之間如要通信的話,必須要經歷下面2個步驟:1. 首先,線程A把本地內存A中更新過的共享變量刷新到主內存中去。2. 然后,線程B到主內存中去讀取線程A之前已更新過的共享變量。
如果A對共享變量進行修改了,在B線程中可以及時的看到修改之后的值,那么我們說對于線程B來說,共享變量對于其是可見的。否則就是不可見的,這就是我們要討論的可見性。
導致共享變量在線程之間不可見的原因:
1、線程的交叉執行 2、重排序結合線程交叉執行 3、共享變量更新后的值沒有在工作內存與主內存間及時更新
很顯然,從上面的結果當中,3.我們是可以理解的。但是為什么線程的交叉執行會導致不可見呢?
舉個例子:
假設存在線程A和線程B,共享變量X,A與B的執行的代碼也是相同的,都是先讀取X,然后將X的值進行修改,再存回去。在這個過程當中,由于線程之間是會競爭CPU資源的,所以在執行到什么語句會喪失CPU的資源是不確定的。假如線程A先讀取了變量X = 1, 而后CPU資源就給B線程搶走了,B對X做了修改,令X++,再存了回去,即X = 2。最后線程A由于JMM的緣故,讀取的X是在自己的工作內存當中,所以仍然是X = 1,再X++,最后X還是等于2。也就是說存在線程執行了兩次X++,但最終X的結果只增加了一次的情況。在這種情況下,內存的可見性就不存在了。
保證可見性的條件如下:
1.共享變量的值的變化是原子語句。
2.共享變量在工作內存和主內存之間需要及時的更新。
1.通過synchronized關鍵字來實現可見性
我們都知道,可以通過synchronized關鍵字來實現同步性。在JMM中是這樣規定的:
1、線程解鎖前,必須把共享變量的最新值刷新到主內存中2、線程加鎖時,將清空工作內存中共享變量的值,從而使用共享變量時需要從主內存中重新讀取最新的值
所以,在synchronized關鍵字的代碼塊中,互斥代碼的實現過程是這樣的:
1、獲得互斥鎖 2、清空工作內存 3、從主內存拷貝變量的最新副本到工作內存 4、執行代碼 5、將更改后的共享變量的值刷新到主內存 6、釋放互斥鎖
由于synchronized關鍵字保證了代碼塊的原子性,又保證了共享變量的及時更新,所以顯然,synchronized關鍵字是可以實現可見性的。
2.通過volatile關鍵字來實現可見性
volatile關鍵字同樣可以實現可見性。
volatile關鍵字的作用如下:
1.對volatile變量執行寫操作時,會在寫操作后加入一條store屏障指令
2.對volatile變量執行讀操作時,會在讀操作前加入一條load屏障指令
也就是說,volatile關鍵字實現了共享變量的及時更新。
但是值得注意的是,volatile關鍵字沒辦法保證語句的原子性,所以如果要實現可見性,需要一個前提條件:共享變量的操作必須是原子性的。
參考:
http://blog.csdn.net/suifeng3051/article/details/52611310 JAVA內存模型
http://www.cnblogs.com/zhilu-doc/p/5778180.html 概括的非常好,囊括了重點
新聞熱點
疑難解答