文章目錄:java
1.簡單理解Volatile和synchronized緩存
2.Volatile詳解多線程
3.synchronized詳解app
4.Volatile與synchronized的區別與聯繫jvm
1.簡單理解Volatile和synchronized:優化
synchronized:當被synchronized修飾時,synchronized會鎖定當前變量,只有當前線程可以訪問該變量,其餘線程會被阻塞。spa
volatile:當volatile修飾變量時,編譯器對此變量的讀寫操做不作任何優化,每次都會去內存去讀,寫也是寫到內存,不作任何的緩存優化線程
volatile,final,synchronized均可以實現可見性。code
2.Volatile詳解對象
volatile可見性原理
可見性:任何線程能立刻看到它的結果
volatile的本質是告訴jvm這個變量是在寄存器中的值是不肯定的,加入volatile關鍵字之後,保存數據會同步到主內存中,而另外一端讀數據時也會從主內存中讀取,volatile使用cpu的緩存鎖來保證可見性。
volatile禁止指令重排序
指令重排序的目的: 提升cpu運算效率
cpu提升利用率的過程:進程 -> 線程 -> 指令
指令重排序究竟是什麼?
舉個栗子:寫代碼每一行都是一個指令,有的指令運行時間比較長,不能讓其餘指令排隊等着,這時對指令進行從新排序,來提升運行效率
無論怎麼重排序,單線程都遵循 as-if-serial語義,而多線程遵循 happen-before
講清楚指令重排序了,volatile如何防止指令重排?
Volatile變量在賦值後會有一個lock add命令,這個命令至關於內存屏障,重排序時不能把屏障後的指令重排序到屏障以前。
volatile使用案例
案例1
在下面這個單例模式下,會指令重排序,若是這時多線程訪問,這時尚未初始化對象,會發生不完整實例。 如圖2-1
使用volatile修飾 instance 防止這種問題發生
public class VolatileDemo { private volatile static VolatileDemo instance; private VolatileDemo(){} /* * 懶漢模式 double check * 不完整實例,指令重排序致使多線程訪問發生不完整實例, * 解決辦法: private volatile static VolatileDemo instance; * 防止指令重排序 * */ public static VolatileDemo getInstance2(){ if (instance == null ){ synchronized (VolatileDemo.class){ if (instance == null){ instance = new VolatileDemo(); } } } return instance; } }
圖2-1
案例2
volatile int i; public void add{ i++; }
這裏先要讀i值 ,而後++,又同步。 再多線程操做時會出錯,原理同案例1,有關指令重排序的問題致使的。
3.synchronized詳解
當被synchronized關鍵字修飾的代碼塊在被編譯成字節碼的時候,會在該代碼塊開始和結束的地方加入 monitorenter 和 moniterexist 指令,任何對象都有一個monitor相關聯,當一個monitor被持有後,他就處於鎖定狀態,當線程執行到monitorenter指令時,會獲嘗試獲取對象對應的monitor的全部權,即獲取對象的鎖。monitor指令如圖3-1
圖3-1
虛擬機在執行這兩個命令的時候,會檢查對象的鎖狀態是否爲空或當前線程是否已經擁有對象的鎖,
若是是 則對象鎖的計數器加 1 ,直接進入同步代碼,
若是不是,則當前線程阻塞等待,等待鎖釋放。
4.Volatile與synchronized的區別與聯繫
(1)volatile,final,synchronized均可以實現可見性
(2)volatile的本質是告訴jvm這個變量是在寄存器中的值是不肯定的,須要從主存中讀取。
(3)synchronized是鎖定當前變量,只有當前線程可以訪問該變量,其餘線程被阻塞。
(4)volatile僅能實現變量的修改可見性,不具有原子性,而synchronized能夠實現變量的修改可見性和原子性
(5)volatile標記的變量不會被編譯器優化,而synchronized能夠(禁止指令重排序)
(6)volatile使用在變量級別,而synchronized可使用在變量和方法
(7)volatile不會阻塞線程,而synchronized可能會