【
尊重
原創,轉載請註明出處】http://blog.csdn.net/guyuealian/article/details/52525724
在說明Java多線程內存可見性以前,先來簡單瞭解一下Java內存模型。
(1)Java全部變量都存儲在主內存中
(2)每一個線程都有本身獨立的工做內存,裏面保存該線程的使用到的變量副本(該副本就是主內存中該變量的一份拷貝)
(1)線程對共享變量的全部操做都必須在本身的工做內存中進行,不能直接在主內存中讀寫
(2)不一樣線程之間沒法直接訪問其餘線程工做內存中的變量,線程間變量值的傳遞須要經過主內存來完成。
線程1對共享變量的修改,要想被線程2及時看到,必須通過以下2個過程:
(1)把工做內存1中更新過的共享變量刷新到主內存中
(2)將主內存中最新的共享變量的值更新到工做內存2中
可見性與原子性
可見性:一個線程對共享變量的修改,更夠及時的被其餘線程看到
原子性:即不可再分了,不能分爲多步操做。好比賦值或者return。好比"a = 1;"和 "return a;"這樣的操做都具備原子性。相似"a += b"這樣的操做不具備原子性,在某些JVM中"a += b"可能要通過這樣三個步驟:
① 取出a和b
② 計算a+b
③ 將計算結果寫入內存
(1)Synchronized:保證可見性和原子性
Synchronized可以實現原子性和可見性;在Java內存模型中,synchronized規定,線程在加鎖時
,先清空工做內存→在主內存中拷貝最新變量的副本到工做內存→執行完代碼→將更改後的共享變量的值刷新到主內存中→釋放互斥鎖。
(2)Volatile:保證可見性,但不保證操做的原子性
Volatile實現內存可見性是經過store和load指令完成的;也就是對volatile變量執行寫操做時,會在寫操做後加入一條store指令,即強迫線程將最新的值刷新到主內存中;而在讀操做時,會加入一條load指令,即強迫從主內存中讀入變量的值。但volatile不保證volatile變量的原子性,例如:
- Private int Num=0;
- Num++;
Num不是原子操做,由於其能夠分爲:讀取Num的值,將Num的值+1,寫入最新的Num的值。
對於Num++;操做,線程1和線程2都執行一次,最後輸出Num的值多是:1或者2
【解釋】輸出結果1的解釋:當線程1執行Num++;語句時,先是讀入Num的值爲0,假若此時讓出CPU執行權,線程得到執行,線程2會從新從主內存中,讀入Num的值仍是0,而後線程2執行+1操做,最後把Num=1刷新到主內存中; 線程2執行完後,線程1由開始執行,但以前已經讀取的Num的值0,因此它仍是在0的基礎上執行+1操做,也就是仍是等於1,並刷新到主內存中。因此最終的結果是1
通常在多線程中使用volatile變量,爲了安全,對變量的寫入操做不能依賴當前變量的值:如Num++或者Num=Num*5這些操做。
(3)Synchronized和Volatile的比較
1)Synchronized保證內存可見性和操做的原子性
2)Volatile只能保證內存可見性
3)Volatile不須要加鎖,比Synchronized更輕量級,並不會阻塞線程(volatile不會形成線程的阻塞;synchronized可能會形成線程的阻塞。)
4)volatile標記的變量不會被編譯器優化,而synchronized標記的變量能夠被編譯器優化(如編譯器重排序的優化).
5)volatile是變量修飾符,僅能用於變量,而synchronized是一個方法或塊的修飾符。
volatile本質是在告訴JVM當前變量在寄存器中的值是不肯定的,使用前,須要先從主存中讀取,所以能夠實現可見性。而對n=n+1,n++等操做時,volatile關鍵字將失效,不能起到像synchronized同樣的線程同步(原子性)的效果。
【相關習題】
(1)下列說法不正確的是()
A.當兩個併發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程獲得執行。
B.當一個線程訪問object的一個synchronized(this)同步代碼塊時,另外一個線程仍然能夠訪問該object中的非synchronized(this)同步代碼塊。
C.當一個線程訪問object的一個synchronized(this)同步代碼塊時,其餘線程對object中全部其它synchronized(this)同步代碼塊的訪問不會被阻塞。
D.當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就得到了這個object的對象鎖。結果,其它線程對該object對象全部同步代碼部分的訪問都被暫時阻塞。
答案:C,當一個線程訪問object的一個synchronized(this)同步代碼塊時,其餘線程對object中全部其它synchronized(this)同步代碼塊的訪問將會被阻塞。
(2)下面敘述錯誤的是: A.經過synchronized和volatile均可以實現可見性 B.不一樣線程之間能夠直接訪問其餘線程工做內存中的變量 C.線程對共享變量的全部操做都必須在本身的工做內存中進行 D.全部的變量都存儲在主內存中 答案:B,不一樣線程之間沒法直接訪問其餘線程工做內存中的變量