(一)記憶體可見性
例子1:
有乙個全域性的狀態變數open:
boolean open=true;
這個變數用來描述對乙個資源的開啟關閉狀態,true表示開啟,false表示關閉,假設有乙個執行緒a,在執行一些操作後將open修改為false:
//執行緒a
resource.close();
open = false;
執行緒b隨時關注open的狀態,當open為true的時候通過訪問資源來進行一些操作:
//執行緒b
while(open)
當a把資源關閉的時候,open變數對執行緒b是不可見的,如果此時open變數的改動尚未同步到執行緒b的工作記憶體中,那麼執行緒b就會用乙個已經關閉了的資源去做一些操作,因此產生錯誤。
下面是乙個通過布林標誌判斷執行緒是否結束的例子:
public
class
cancelthreadtest finally
}}class
primegenerator
implements
runnable
}public
void cancel()
}主線程中設定primegenerator執行緒的是否取消標識,primegenerator執行緒檢測到這個標識後就會結束執行緒,由於主線程修改cancelled變數的記憶體可見性,主線程修改cancelled標識後並不馬上同步回主記憶體,所以primegenerator執行緒結束的時間難以把控(最終是一定會同步回主記憶體,讓primegenerator執行緒結束)。
如果primegenerator執行緒執行一些比較關鍵的操作,主線程希望能夠及時終止它,這時將cenceled用volatile關鍵字修飾就是必要的。
特別注意:上面演示這個並不是正確的取消執行緒的方法,因為一旦primegenerator執行緒中包含bolckingqueue.put()等阻塞方法,那麼將可能永遠不會去檢查cancelled標識,導致執行緒永遠不會退出。
(二)防止指令重排
對於在同乙個執行緒內,這樣的改變是不會對邏輯產生影響的,但是在多執行緒的情況下指令重排序會帶來問題。看下面這個情景:
context = loadcontext();
inited = true;
while(!inited )
dosomethingwithconfig(context);
假設執行緒a中發生了指令重排序:
inited = true;
context = loadcontext();
那麼b中很可能就會拿到乙個尚未初始化或尚未初始化完成的context,從而引發程式錯誤。
我們都知道乙個經典的懶載入方式的雙重判斷單例模式:public
class singleton
public
static singleton getinstance() }}
return instance;
}}看似簡單的一段賦值語句:instance= new singleton(),但是很不幸它並不是乙個原子操作,其實際上可以抽象為下面幾條jvm指令:
memory =allocate(); //1:分配物件的記憶體空間
ctorinstance(memory); //2:初始化物件
上面操作2依賴於操作1,但是操作3並不依賴於操作2,所以jvm是可以針對它們進行指令的優化重排序的,經過重排序後如下:
memory =allocate(); //1:分配物件的記憶體空間
ctorinstance(memory); //2:初始化物件
可以看到指令重排之後,instance指向分配好的記憶體放在了前面,而這段記憶體的初始化被排在了後面。
(三)總結
volatile使用建議:
(四)volatile和synchronized的區別
volatile 關鍵字的如何保證記憶體可見性
volatile關鍵字的作用 保證記憶體的可見性 防止指令重排 注意 volatile 並不保證原子性 記憶體可見性 volatile保證可見性的原理是在每次訪問變數時都會進行一次重新整理,因此每次訪問都是主記憶體中最新的版本。所以volatile關鍵字的作用之一就是保證變數修改的實時可見性。當且僅...
volatile關鍵字是如何保證程式的可見性
volatile被喻為輕量級的 synchronized 雖然一定程度上要比synchronized關鍵字效率要高,但它也有不足之處,就是不具有互斥性和原子性。volatile關鍵字的主要作用就是保證各執行緒之間的可見性,意思就是在多執行緒環境下,某個共享變數如果被其中乙個執行緒給修改了,其他執行緒...
如何理解volatile關鍵字
暫時由於查了好多網路上的資料都沒有具體的說明,有範圍也很大沒有看懂,後面如果理解有錯再修改。1.全域性共享變數非volatile 我是這樣理解的,對於多執行緒中,多個執行緒啟動時,部分先啟動的執行緒會把全域性變數拷貝乙個副本到自己的執行緒棧,有的則是啟動 還沒有read load變數到自己的本地棧空...