java併發對volatile的理解

談到併發,離不開java裡面的幾個關鍵字volatitle和synchronized,下面介紹下volatitle的原理以及應用的幾個簡單場景

首先簡單的介紹下volatile的兩個作用 1.線程可見性 2.禁止重排序,舉個例子,變量自增a初始值為1

1.假設A線程首次讀取變量a,先從主內存中讀取變量a的值,然後存入工作內存,以後只需要在工作內存中讀取a變量

2.如果進行修改操作,先將新值寫入線程副本,再刷新到主內存中(刷新時間不確定)

3.假設B線程也是首次讀取變量a,先從主內存中讀取變量a的值,但是因為上述第二步可能還未更新到主內存,讀到的值為更新前的值1

4.上述情況導致2個線程執行完成之後值還是2

再來看如果給a變量加上volatile修飾的情況,同樣是上面提到的例子,文章中提到volatile具有線程可見性,但是還是不能保證併發情況的數據準確性

1.假設A線程首次讀取變量a,先從主內存中讀取變量a的值,然後存入工作內存(工作內存的值還是要記錄的,當其他線程對a進行修改時,會導致當前線程的內存中的變量副本失效),

2.如果進行修改操作,先將新值寫入線程副本,修改結束直接刷新到主內存中

3.假設B線程也是首次讀取變量a,先從主內存中讀取變量a的值,因為上述第二步已經更新到主內存,讀到的值為更新前的值2

4.上述情況2個線程執行完成之後值為3

但是為什麼還是會存在值為2的場景產生,原因如下:因為上述的例子中的第二步的自增是非原子性操作,可能在A線程讀取到a值的時候,下一步切換到線程B,B線程繼續讀取a值為1,執行加1操作,刷新到主內存,值為2,然後再切回A線程,A線程由於之前已經執行完讀取操作,值為1,等執行加1操作後,刷新a值到主內存,導致執行完2個線程值還是2,所以volatitle保證了線程的可見性,但是中間步驟涉及非原子性操作的時候,還是會出問題。


接下來看下單例的例子以及簡單的分析下重排序

單例模式大家應該都比較熟悉,單例模式中有個雙重檢查模式,代碼如下:

class Singleton{

private static Singleton instance = null;

private Singleton() {


}

public static Singleton getInstance() {

if( instance==null ) {

synchronized ( Singleton.class ) {

if( instance == null )

instance = new Singleton();

}

}

return instance;

}

}

上述代碼存在問題,可能會拋異常,因為instance = new Singleton()不是原子性,總共分為3步(1)分配內存(2)初始化對象(3)對象引用賦值給變量,jvm運行時,第2和第3步可能會發生重排序,就是說會先執行(3)再執行(2),這種情況下,其他線程,在判斷instance==null的時候是不成立的,因為已經執行第(3)步,但是又由於對象還未初始化,導致線程會出異常

,所以加上volatitle關鍵字,禁止重排序,(2)(3)步驟按順序執行,就不會又問題

class Singleton{

private volatitle static Singleton instance = null;

private Singleton() {


}

public static Singleton getInstance() {

if( instance==null ) {

synchronized ( Singleton.class ) {

if( instance == null )

instance = new Singleton();

}

}

return instance;

}

}


分享到:


相關文章: