上一篇文章中咱們介紹了 Synchronzied ,接下來咱們來介紹 volatile。安全
咱們知道 Java 內存模型有三大特性,有序性、可見性、原子性。bash
那麼什麼是有序性呢?Talk is cheap,show me the code 咱們直接來看代碼。多線程
public static void main(String[] args) {
int num1=10;//代碼1
int num2=5;//代碼2
num1=num1-num2;//代碼3
num2=num1*num1;//代碼4
}
複製代碼
這段代碼的執行順序必定是代碼一、二、三、4 嗎?這是不必定的,爲何呢?由於可能會發生指令重排序。指令重排序就是在不影響程序最終結果的前提下,爲了提高程序的執行效率,可能會對輸入的代碼順序進行優化,形成執行順序不必定按照代碼書寫順序的一種形式。優化
咱們來看看指令重排序的定義,一個必要的條件就是不影響程序的執行結果,那麼想一想看咱們的代碼3和代碼4的執行順序可否調換順序呢?答案天然是否認的,由於代碼4須要依賴代碼3的內容。spa
在單線程的狀況下,重排序不會影響程序的運行結果,那麼在多線程狀況下呢?咱們來看一看。線程
//線程1:
context = loadContext(); //語句1
inited = true; //語句2
//線程2:
while(!inited ){
sleep()
}
doSomethingwithconfig(context);
複製代碼
首先在線程1中先執行語句2,而後切換到線程2來執行。這個時候線程二認爲初始化已經完成,就會進行接下來的操做,可是 context 並無初始化,因此就會致使代碼出錯。code
可見性是指當一個線程修改了共享變量後,其餘線程可以當即得知這個修改。排序
咱們使用 volatile 關鍵字能夠保證可見性,使得各個線程均可以讀取到最新的值內存
原子性是指一個操做是不可中斷的,要麼所有執行成功要麼所有執行失敗,有着「同生共死」的感受。及時在多個線程一塊兒執行的時候,一個操做一旦開始,就不會被其餘線程所幹擾。it
咱們來看一個最簡單的例子,代碼 i++ 是原子性操做嗎?其實 i++ 能夠分爲3步來看待。
這其中任何一步發生均可能發生線程安全問題,因此 i ++ 不是原子性操做。
介紹完咱們的三大特性,咱們回過頭再來講 volatile , volatile 關鍵字能夠作到可見性和有序性。也就是能夠保證變量獲取最新的值以及禁止指令重排序,可是它不能實現原子性,因此 volatile 的使用具備侷限性,咱們簡單地來總結一下它的使用場景。