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