前言
??Volatile的關(guān)鍵字從Java5出現(xiàn)后就存在了,很多人做了很長時(shí)間的Java開發(fā)都未必用的到這個(gè)關(guān)鍵字,其實(shí)它還是很有用的一個(gè)關(guān)鍵字。
正文
簡單概述
??volatile是Java提供的一種輕量級(jí)的同步機(jī)制,同synchronized相比,volatile更輕量級(jí),在訪問volatile變量時(shí)不會(huì)執(zhí)行加鎖操作,因此也就不會(huì)使執(zhí)行的線程阻塞。
變量不加volatile
問題1 可見性問題
??CPU每次執(zhí)行指令的時(shí)候會(huì)對(duì)內(nèi)存進(jìn)行讀寫操作,盡管內(nèi)存的讀寫速度比硬盤的讀寫速度快得多,但是指令的處理速度更快,從內(nèi)存進(jìn)行讀寫速度相對(duì)于指令執(zhí)行的速度就慢了,所以CPU就會(huì)將線程用到的變量以及一些復(fù)雜的操作復(fù)制到緩存(寄存器)中處理,然后再將處理的結(jié)果寫入內(nèi)存。但是對(duì)于多核CPU,線程可能運(yùn)行在不同的CPU緩存中,這樣的話就無法保證線程的可見性(指線程之間的可見性,一個(gè)線程修改的變量后,另一個(gè)線程取到變量也是修改后的變量)。

//初始化
int i =10;
//每個(gè)線程都對(duì)i++
i++;
??對(duì)于i來說,線程A從內(nèi)存讀取i后存入緩存,它取到的值是10,線程B也將i從內(nèi)存中讀入緩存,取到值也是10。線程A對(duì)i++,i就變成了11,但是線程B中的i還是10,那么i變量對(duì)于線程是不可見的了。
問題2 有序性問題
int i = 0;//A步驟
int j = 1;//B步驟
i++;//C步驟
j++;//D步驟
??CPU為了保證性能或者在多個(gè)線程存在的情況下,對(duì)于指令的處理順序會(huì)有變化(指令重排序:CPU允許將多條指令不按程序規(guī)定的順序分別給各個(gè)相應(yīng)的處理器處理),比如它可能先執(zhí)行了B步驟,然后在執(zhí)行A步驟,也可能先執(zhí)行了D步驟,然后再執(zhí)行C步驟。
變量加volatile
線程可見性
//初始化
volatile int i =10;
??對(duì)于一個(gè)變量賦予了volatile關(guān)鍵字后那么變量對(duì)所有的線程具有可見性,當(dāng)一個(gè)線程修改了這個(gè)變量的值,編譯器就不會(huì)將這個(gè)變量復(fù)制到緩存中,就直接在內(nèi)存中操作,這樣從內(nèi)存中取到的volatile標(biāo)識(shí)的變量就是最新的值,也就對(duì)其它線程可見了。

線程有序性
??volatile可以禁止指令重排序,volatile標(biāo)識(shí)的變量,會(huì)有內(nèi)存屏障,指令重排序時(shí)就不能把后面的指令重排序到內(nèi)存屏障之前的位置,這樣就保證不同的代碼塊只能串行執(zhí)行,不能同步執(zhí)行,這樣就保證了線程的有序性。
volatile缺點(diǎn)
volatile不能保證原子性,volatile可以配合synchronized保證原子性。