天天看點

記憶體可見性和原子性:Synchronized和Volatile的比較Java多線程之記憶體可見性和原子性:Synchronized和Volatile的比較

Java多線程之記憶體可見性和原子性:Synchronized和Volatile的比較

【尊重原創,轉載請注明出處】http://blog.csdn.net/guyuealian/article/details/52525724

在說明Java多線程記憶體可見性之前,先來簡單了解一下Java記憶體模型。

(1)Java所有變量都存儲在主記憶體中

(2)每個線程都有自己獨立的工作記憶體,裡面儲存該線程的使用到的變量副本(該副本就是主記憶體中該變量的一份拷貝)

記憶體可見性和原子性:Synchronized和Volatile的比較Java多線程之記憶體可見性和原子性:Synchronized和Volatile的比較

(1)線程對共享變量的所有操作都必須在自己的工作記憶體中進行,不能直接在主記憶體中讀寫

(2)不同線程之間無法直接通路其他線程工作記憶體中的變量,線程間變量值的傳遞需要通過主記憶體來完成。

線程1對共享變量的修改,要想被線程2及時看到,必須經過如下2個過程:

(1)把工作記憶體1中更新過的共享變量重新整理到主記憶體中

(2)将主記憶體中最新的共享變量的值更新到工作記憶體2中

記憶體可見性和原子性:Synchronized和Volatile的比較Java多線程之記憶體可見性和原子性:Synchronized和Volatile的比較

可見性與原子性

可見性:一個線程對共享變量的修改,更夠及時的被其他線程看到

原子性:即不可再分了,不能分為多步操作。比如指派或者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變量的原子性,例如:

[java] view plain copy

  1. Private int Num=0;
  2. Num++;//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,不同線程之間無法直接通路其他線程工作記憶體中的變量