vilatile 深入理解java虚拟机_《深入理解Java虚拟机》笔记 第十二章 volatile变量
當(dāng)一個變量定義成volatile之后,它將具備兩種特性:
1、第一是保證此變量對所有線程的可見性,這里的"可見性"是指當(dāng)一條線程修改了這個變量的值,新值對于其它線程是可以立即得知的,變量值在線程間傳遞均需要通過主內(nèi)存來完成,如:線程A修改一個普通變量的值,然后向主內(nèi)存進(jìn)行回寫,另外一條線程B在線程A回寫完成了之后再從主內(nèi)存進(jìn)行讀取操作,新變量的值才會對線程B可見。
2、使用volatile變量的第二個語義是禁止指令重排序優(yōu)化,變通的變量僅僅會保證在該方法的執(zhí)行過程中所有依賴賦值結(jié)果的地方能獲取到正確的結(jié)果,而不能保證變量的賦值操作的順序與程序代碼中的執(zhí)行順序一致。
因?yàn)樵谝粋€線程的方法執(zhí)行過程中無法感知到這一點(diǎn),這也就是Java內(nèi)存模型中描述的所謂的"線程內(nèi)表現(xiàn)為串行的語義"(Within-Thread As-If-Serial Sematics)。
關(guān)于volatile變量的可見性,很多人誤以為以下描述成立:"volatile對所有線程是立即可見的,對volatile變量所有的寫操作都能立即返回到其它線程之中,換句話說,volatile變量在各個線程中是一致的,所以基于volatile變量的運(yùn)算在并發(fā)下是安全的"。
這句話的論據(jù)部分并沒有錯,但是其論據(jù)并不能得出"基于volatile變量的運(yùn)算在并發(fā)下是安全的"這個結(jié)論。
volatile變量在各個線程的工作內(nèi)存中不存在一致性問題(在各個線程的工作內(nèi)存中volatile變量也可以存在不一致的情況,但由于每次使用之前都要先刷新,執(zhí)行引擎看不到不致的情況,因此可以認(rèn)為不存在一致性問題),但是Java里的運(yùn)算并非原子操作,導(dǎo)致volatile變量的運(yùn)算在并發(fā)下一樣是不安全的。
由于volatile變量只能保證可見性,在不符合以下條件規(guī)則的去處場景中,仍然需要通過加鎖來保證原子性。
1.運(yùn)算結(jié)果不依賴變量的當(dāng)前值,或者能確保只有單一的線程改變變量的值。
2.變量不需要與其它的狀態(tài)變量共同參與不變約束。
為何指令重排會干擾程序的并發(fā)執(zhí)行
例子
Map configOptions;
char[] configText;
//此變量必須定義為volatile
volatile boolean initialized = false;
//假設(shè)以下代碼在線程A中執(zhí)行
//模擬讀取配置信息,當(dāng)讀取完成后
//將initialized設(shè)置為true來通知其它線程配置可用
configOptions = new HashMap();
configText = readConfigFile(fileName);
processConfigOptions(configText, configOptions);
initialized = true;
//假設(shè)以下代碼在線程B中執(zhí)行
//等線程A待initialized為true,代表線程A已經(jīng)把配置信息初始化完成
while(!initialized) {
sleep();
}
//使用線程A中初始化好的配置信息
doSomethingWithConfig();
上面為一段偽代碼,其中描述的場景十分常見,只是我們在處理配置文件時一般不會出現(xiàn)并發(fā)而已。如果定義initialized變量時沒有使用volatile修飾,就可能會由于指令重排序的優(yōu)化,導(dǎo)致位于線程A中最后一句的代碼"initialized = true"被提前執(zhí)行,這樣在線程B中使用配置信息的代碼就可能出現(xiàn)錯誤,而volatile關(guān)鍵字則可以避免此類情況的發(fā)生。
Java內(nèi)存模型中對volatile變量定義的特殊規(guī)則。假定T表示一個線程,V和W分別表示兩個volatile變量,那么在進(jìn)行read、load、use、assign、store、write操作時需要滿足如下的規(guī)則:
1.只有當(dāng)線程T對變量V執(zhí)行的前一個動作是load的時候,線程T才能對變量V執(zhí)行use動作;并且,只有當(dāng)線程T對變量V執(zhí)行的后一個動作是use的時候,線程T才能對變量V執(zhí)行l(wèi)oad操作。線程T對變量V的use操作可以認(rèn)為是與線程T對變量V的load和read操作相關(guān)聯(lián)的,必須一起連續(xù)出現(xiàn)。這條規(guī)則要求在工作內(nèi)存中,每次使用變量V之前都必須先從主內(nèi)存刷新最新值,用于保證能看到其它線程對變量V所作的修改后的值。
2.只有當(dāng)線程T對變量V執(zhí)行的前一個動是assign的時候,線程T才能對變量V執(zhí)行store操作;并且,只有當(dāng)線程T對變量V執(zhí)行的后一個動作是store操作的時候,線程T才能對變量V執(zhí)行assign操作。線程T對變量V的assign操作可以認(rèn)為是與線程T對變量V的store和write操作相關(guān)聯(lián)的,必須一起連續(xù)出現(xiàn)。這一條規(guī)則要求在工作內(nèi)存中,每次修改V后都必須立即同步回主內(nèi)存中,用于保證其它線程可以看到自己對變量V的修改。
3.假定操作A是線程T對變量V實(shí)施的use或assign動作,假定操作F是操作A相關(guān)聯(lián)的load或store操作,假定操作P是與操作F相應(yīng)的對變量V的read或write操作;類型地,假定動作B是線程T對變量W實(shí)施的use或assign動作,假定操作G是操作B相關(guān)聯(lián)的load或store操作,假定操作Q是與操作G相應(yīng)的對變量V的read或write操作。如果A先于B,那么P先于Q。這條規(guī)則要求valitile修改的變量不會被指令重排序優(yōu)化,保證代碼的執(zhí)行順序與程序的順序相同。
總結(jié)
以上是生活随笔為你收集整理的vilatile 深入理解java虚拟机_《深入理解Java虚拟机》笔记 第十二章 volatile变量的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: class unity 定义类_Unit
- 下一篇: java美元兑换,(Java实现) 美元