記憶體可見性和原子性:Synchronized和Volatile的比較
Java多執行緒之記憶體可見性和原子性:Synchronized和Volatile的比較
【尊重原創,轉載請註明出處】http://blog.csdn.net/guyuealian/article/details/52525724
在說明Java多執行緒記憶體可見性之前,先來簡單瞭解一下Java記憶體模型。
(1)Java所有變數都儲存在主記憶體中
(2)每個執行緒都有自己獨立的工作記憶體,裡面儲存該執行緒的使用到的變數副本(該副本就是主記憶體中該變數的一份拷貝)
(2)每個執行緒都有自己獨立的工作記憶體,裡面儲存該執行緒的使用到的變數副本(該副本就是主記憶體中該變數的一份拷貝)
(1)執行緒對共享變數的所有操作都必須在自己的工作記憶體中進行,不能直接在主記憶體中讀寫
(2)不同執行緒之間無法直接訪問其他執行緒工作記憶體中的變數,執行緒間變數值的傳遞需要通過主記憶體來完成。
執行緒1對共享變數的修改,要想被執行緒2及時看到,必須經過如下2個過程:
(1)把工作記憶體1中更新過的共享變數重新整理到主記憶體中
(2)將主記憶體中最新的共享變數的值更新到工作記憶體2中
(2)不同執行緒之間無法直接訪問其他執行緒工作記憶體中的變數,執行緒間變數值的傳遞需要通過主記憶體來完成。
執行緒1對共享變數的修改,要想被執行緒2及時看到,必須經過如下2個過程:
(1)把工作記憶體1中更新過的共享變數重新整理到主記憶體中
(2)將主記憶體中最新的共享變數的值更新到工作記憶體2中
可見性與原子性
可見性:一個執行緒對共享變數的修改,更夠及時的被其他執行緒看到
原子性:即不可再分了,不能分為多步操作。比如賦值或者return。比如"a = 1;"和 "return a;"這樣的操作都具有原子性。類似"a += b"這樣的操作不具有原子性,在某些JVM中"a += b"可能要經過這樣三個步驟:
① 取出a和b
② 計算a+b
③ 將計算結果寫入記憶體
原子性:即不可再分了,不能分為多步操作。比如賦值或者return。比如"a = 1;"和 "return a;"這樣的操作都具有原子性。類似"a += b"這樣的操作不具有原子性,在某些JVM中"a += b"可能要經過這樣三個步驟:
① 取出a和b
② 計算a+b
③ 將計算結果寫入記憶體
(1)Synchronized:保證可見性和原子性
Synchronized能夠實現原子性和可見性;在Java記憶體模型中,synchronized規定,執行緒在加鎖時,先清空工作記憶體→在主記憶體中拷貝最新變數的副本到工作記憶體→執行完程式碼→將更改後的共享變數的值重新整理到主記憶體中→釋放互斥鎖。
Synchronized能夠實現原子性和可見性;在Java記憶體模型中,synchronized規定,執行緒在加鎖時,先清空工作記憶體→在主記憶體中拷貝最新變數的副本到工作記憶體→執行完程式碼→將更改後的共享變數的值重新整理到主記憶體中→釋放互斥鎖。
(2)Volatile:保證可見性,但不保證操作的原子性
Volatile實現記憶體可見性是通過store和load指令完成的;也就是對volatile變數執行寫操作時,會在寫操作後加入一條store指令,即強迫執行緒將最新的值重新整理到主記憶體中;而在讀操作時,會加入一條load指令,即強迫從主記憶體中讀入變數的值。但volatile不保證volatile變數的原子性,例如:
Private int Num=0;
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一樣的執行緒同步(原子性)的效果。
【參考資料】《細說Java多執行緒之記憶體可見性》http://www.imooc.com/video/6775(含視訊和程式碼)
【相關習題】
(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,不同執行緒之間無法直接訪問其他執行緒工作記憶體中的變數
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,不同執行緒之間無法直接訪問其他執行緒工作記憶體中的變數
相關文章
- volatile記憶體可見性和指令重排記憶體
- 10-Java中共享記憶體可見性以及synchronized和volatile關鍵字Java記憶體synchronized
- java安全編碼指南之:可見性和原子性Java
- 「跬步千里」詳解 Java 記憶體模型與原子性、可見性、有序性Java記憶體模型
- Synchronized同步性與可見性synchronized
- Synchronized、lock、volatile、ThreadLocal、原子性總結、Conditionsynchronizedthread
- 走進volatile的世界,探索它與可見性,有序性,原子性之間的愛恨情仇!
- JVM併發機制探討—記憶體模型、記憶體可見性和指令重排序JVM記憶體模型排序
- Volatile可見性分析(一)
- Java併發之原子性、有序性、可見性Java
- 執行緒安全性-原子性、可見性、有序性執行緒
- Java併發程式設計Bug源頭:可見性、原子性和有序性問題Java程式設計
- 三大性質總結:原子性、可見性以及有序性
- 深入理解Java多執行緒與併發框(第③篇)——Java記憶體模型與原子性、可見性、有序性Java執行緒記憶體模型
- java多執行緒3:原子性,可見性,有序性Java執行緒
- 【雜談】快取記憶體一致性與可見性快取記憶體
- 高階java必須清楚的概念:原子性、可見性、有序性Java
- 理解併發程式設計的幾種"性" -- 可見性,有序性,原子性程式設計
- 併發程式設計基礎 - 管程模型和synchronized原子性程式設計模型synchronized
- 全域性索引和本地索引的比較索引
- [深入理解Java虛擬機器]原子性/可見性/有序性Java虛擬機
- 【Java併發入門】02 Java記憶體模型:看Java如何解決可見性和有序性問題Java記憶體模型
- Java併發程式設計-併發程式設計的Bug源頭:可見性、原子性和有序性問題Java程式設計
- synchronized和volatile理解synchronized
- 面試官:volatile如何保證可見性的,具體如何實現?面試
- C和C++中的volatile、記憶體屏障和CPU快取一致性協議MESIC++記憶體快取協議
- 深入理解JVM(二)——記憶體模型、可見性、指令重排序JVM記憶體模型排序
- synchronized和volatile的區別synchronized
- volatile和synchronized的區別synchronized
- 使用 volatile 關鍵字保證變數可見性和禁止指令重排序變數排序
- Synchronized和ReentrantLock鎖機制的比較synchronizedReentrantLock
- Volatile不保證原子性及解決方案
- 記一次對Java多執行緒記憶體可見性的測試Java執行緒記憶體
- Solidity之旅(十三)函式及其可見性和狀態可變性Solid函式
- Java併發程式設計-volatile可見性的介紹Java程式設計
- 淺談併發的資料競爭(可見性)與競態條件(原子性)
- Solidity語言學習筆記————22、可見性和GettersSolid筆記
- Java記憶體模型(MESI、記憶體屏障、volatile和鎖及final記憶體語義)Java記憶體模型