volatile是java提供的一種輕量級的同步機制,可以理解為一個變量的同步鎖映之。相比于重量級鎖synchronized而言便锨,synchronized大部分時候都是鎖的方法或者代碼塊歪架,而volatile只是鎖一個變量,所以volatile更加輕量葱椭,當然現(xiàn)在jdk對synchronized的性能越來越優(yōu)化捂寿,也沒有想象中的那么重,在平時需要保證一個方法線程安全時孵运,可以放心的使用synchronized秦陋。
原理
計算機系統(tǒng)在進行計算時,cpu會從內(nèi)存中讀取數(shù)據(jù)緩存在自己的寄存器(cpu中空間很小的一塊內(nèi)存空間治笨,方便取值計算驳概,不用每次都讀取內(nèi)存)中,現(xiàn)在大部分計算機都是多核計算機旷赖,有多個cpu顺又,這里的寄存器對其他cpu是不可見的,如下圖
當把一個變量聲明為volatile類型后等孵,這個變量不會被緩存在cpu的寄存器中稚照,每次都是直接操作的內(nèi)存空間,因此其他線程每次讀取的都是最新的值俯萌。
特性
- 變量可見性:volatile變量對所有線程都是可見的果录,一個線程修改了變量的值,那么其他線程會立即獲取這個新值咐熙。
- 禁止指令重排序:在jvm編譯器和cpu中弱恒,有時候會為了優(yōu)化效率會對正常的操作指令進行重新排序,volatile變量會禁止指令重排序棋恼。
舉例
我們熟悉的單例模式中返弹,有一種安全的寫法,如下雙重檢查的寫法
public class Single {
private static volatile Single single = null;
private Single() {
}
public static Single getInstance() {
if (single == null) {
synchronized (single) {
if (single == null) {
single = new Single();
}
}
}
return single;
}
}
代碼中將Single對象聲明成了volatile對象爪飘,如果沒有volatile會出現(xiàn)什么情況呢义起?我們看一下single = new Single()這句代碼,正常的操作會是這樣:
- 分配內(nèi)存地址M师崎;
- 在內(nèi)存M上初始化Single對象并扇;
- 將M的地址賦值給single對象;
但編譯期會出現(xiàn)指令重排,操作會變成這樣:
- 分配內(nèi)存地址M穷蛹;
- 將M的地址賦值給single對象;
- 在內(nèi)存M上初始化Single對象昼汗;
回到代碼中肴熏,當A線程執(zhí)行single = new Single()中的第二步時,B線程進入getInstance()方法顷窒,先判斷 if (single == null)蛙吏,這時single對象上已經(jīng)有內(nèi)存地址了,B線程會直接返回single對象鞋吉,但是這個線程拿到的是個空對象鸦做,內(nèi)存M上還沒有初始化對象。
將single變量聲明成volatile時谓着,就會避免這類指令重排的問題泼诱。
結(jié)論
當我們程序中出現(xiàn)可能多個線程操作同一個共享變量時,要保證這個共享變量的線程安全赊锚,可以將這個變量聲明成volatile治筒。