什么是volatile的可見性?
首先要從JMM內(nèi)存模型說起,每一個(gè)線程都會(huì)有自己的工作內(nèi)存,而線程要訪問主內(nèi)存的共享變量時(shí),是會(huì)將變量對(duì)象拷貝一份變量副本到工作內(nèi)存。
對(duì)于普通共享變量,線程讀取的變量值是工作內(nèi)存的副本,修改后并非立即寫入主內(nèi)存,因此在多線程并發(fā)的情況,一個(gè)線程修改了變量值后,另一個(gè)線程可能依然讀取的是自己工作內(nèi)存的變量副本,而沒有讀到另一個(gè)線程修改后的變量值。因此就會(huì)造成并發(fā)情況下出現(xiàn)邏輯控制上的問題。
對(duì)于volatile修飾的變量,線程在修改工作內(nèi)存的變量副本時(shí),會(huì)置其他線程工作內(nèi)存中該變量副本為無效,并在修改后立即寫入主內(nèi)存。而其他線程在發(fā)現(xiàn)工作內(nèi)存中變量副本置為無效時(shí)會(huì)去主內(nèi)存讀取最新的值,這個(gè)過程會(huì)去等待修改線程先寫入主內(nèi)存。因此volatile修飾變量后,線程的讀寫效率會(huì)降低。
volatile修飾變量后,可能引入的問題:因?yàn)関olatile修飾后,變量修改后會(huì)立即寫入到主內(nèi)存。所以可能存在一個(gè)線程內(nèi)已經(jīng)對(duì)該變量判空,但是因?yàn)榱硪粋€(gè)線程置空,導(dǎo)致本線程更容易發(fā)生空指針異常。
volatile A a = new A();
線程1
a = null;
線程2
if(a != null){
a.b();// 可能空指針異常
}
什么是volatile的有序性?
說到有序性就不得不說說指令重排序,什么是指令重排序?簡單的說就是系統(tǒng)為了優(yōu)化代碼執(zhí)行效率,會(huì)打亂代碼先后順序并可能先執(zhí)行后面的代碼。
舉個(gè)例子:
int a=1; // 代碼1
int b =2;// 代碼2
int c =a+b; // 代碼3
代碼2可能會(huì)比代碼1先執(zhí)行,但是代碼3不會(huì)比前面的先執(zhí)行,因?yàn)檫@樣會(huì)出錯(cuò),代碼3依賴前兩者的結(jié)果。所以系統(tǒng)在保證邏輯正確的情況下會(huì)對(duì)代碼重排序。
而volatile修飾的變量就可以禁止指令重排序。
舉個(gè)例子:
//變量c是volatile類型
int a =1;//代碼1
boolean flag =true; //代碼2
c =2;//代碼3
a =2;//代碼4
flag =false; //代碼5
代碼1和代碼2可能重排序,代碼4和代碼5可能重排序,但是代碼3不會(huì)先于代碼1和2執(zhí)行,也不會(huì)在代碼4和5之后執(zhí)行。