volatile這個(gè)關(guān)鍵字可能很多朋友都聽說過,或許也都用過。在Java 5之前,它是一個(gè)備受爭議的關(guān)鍵字,因?yàn)樵诔绦蛑惺褂盟鶗?dǎo)致出人意料的結(jié)果。在Java 5之后,volatile關(guān)鍵字才得以重獲生機(jī)。
volatile 關(guān)鍵字作用是,使系統(tǒng)中所有線程對該關(guān)鍵字修飾的變量共享可見,可以禁止線程的工作內(nèi)存對volatile修飾的變量進(jìn)行緩存。
volatile 2個(gè)使用場景:
1.可見性:Java提供了volatile關(guān)鍵字來保證可見性。
當(dāng)一個(gè)共享變量被volatile修飾時(shí),它會保證修改的值會立即被更新到主存,當(dāng)有其他線程需要讀取時(shí),它會去內(nèi)存中讀取新值。
而普通的共享變量不能保證可見性,因?yàn)槠胀ü蚕碜兞勘恍薷闹螅裁磿r(shí)候被寫入主存是不確定的,當(dāng)其他線程去讀取時(shí),此時(shí)內(nèi)存中可能還是原來的舊值,因此無法保證可見性。
另外,通過synchronized和Lock也能夠保證可見性,synchronized和Lock能保證同一時(shí)刻只有一個(gè)線程獲取鎖然后執(zhí)行同步代碼,并且在釋放鎖之前會將對變量的修改刷新到主存當(dāng)中。因此可以保證可見性。
先看一段代碼,假如線程1先執(zhí)行,線程2后執(zhí)行:
1
2
3
4
5
6
7
|
//線程1 boolean stop = false ; while (!stop){ doSomething(); } //線程2 stop = true ; |
這段代碼是很典型的一段代碼,很多人在中斷線程時(shí)可能都會采用這種標(biāo)記辦法。但是事實(shí)上,這段代碼會完全運(yùn)行正確么?即一定會將線程中斷么?不一定,也許在大多數(shù)時(shí)候,這個(gè)代碼能夠把線程中斷,但是也有可能會導(dǎo)致無法中斷線程(雖然這個(gè)可能性很小,但是只要一旦發(fā)生這種情況就會造成死循環(huán)了)。
下面解釋一下這段代碼為何有可能導(dǎo)致無法中斷線程。在前面已經(jīng)解釋過,每個(gè)線程在運(yùn)行過程中都有自己的工作內(nèi)存,那么線程1在運(yùn)行的時(shí)候,會將stop變量的值拷貝一份放在自己的工作內(nèi)存當(dāng)中。
那么當(dāng)線程2更改了stop變量的值之后,但是還沒來得及寫入主存當(dāng)中,線程2轉(zhuǎn)去做其他事情了,那么線程1由于不知道線程2對stop變量的更改,因此還會一直循環(huán)下去。
但是用volatile修飾之后就變得不一樣了:
第一:使用volatile關(guān)鍵字會強(qiáng)制將修改的值立即寫入主存;
第二:使用volatile關(guān)鍵字的話,當(dāng)線程2進(jìn)行修改時(shí),會導(dǎo)致線程1的工作內(nèi)存中緩存變量stop的緩存行無效(反映到硬件層的話,就是CPU的L1或者L2緩存中對應(yīng)的緩存行無效);
第三:由于線程1的工作內(nèi)存中緩存變量stop的緩存行無效,所以線程1再次讀取變量stop的值時(shí)會去主存讀取。
那么在線程2修改stop值時(shí)(當(dāng)然這里包括2個(gè)操作,修改線程2工作內(nèi)存中的值,然后將修改后的值寫入內(nèi)存),會使得線程1的工作內(nèi)存中緩存變量stop的緩存行無效,然后線程1讀取時(shí),發(fā)現(xiàn)自己的緩存行無效,它會等待緩存行對應(yīng)的主存地址被更新之后,然后去對應(yīng)的主存讀取最新的值。
那么線程1讀取到的就是最新的正確的值。
2.保證有序性
1
2
3
4
5
6
7
8
9
|
volatile boolean inited = false ; //線程1: context = loadContext(); inited = true ; //線程2: while (!inited ){ sleep() } doSomethingwithconfig(context); |
確保context已經(jīng)初始化完成。
3.double check
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class Singleton{ private volatile static Singleton instance = null ; private Singleton() { } public static Singleton getInstance() { if (instance== null ) { synchronized (Singleton. class ) { if (instance== null ) instance = new Singleton(); } } return instance; } } |
以上所述是小編給大家介紹的Java中volatile關(guān)鍵字的作用與用法詳解,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時(shí)回復(fù)大家的。在此也非常感謝大家對服務(wù)器之家網(wǎng)站的支持!
原文鏈接:http://www.cnblogs.com/eoss/archive/2016/09/29/5920241.html