java volatile關鍵字的討論

//一個volatile的用例
//Thread A
init();
initlized= true;

//Thread B
while(!initlized){
    sleep();
}
//volatile變量的作用如下,保證了線程B使用initlized變量時,從
主線程讀取,而不是使用工作線程的變量(變量可見性)。保證了線程A
中的initlized=true會在init()方法後執行(禁止指令重排序)。

volatile關鍵字的語義主要是以下兩個:

  • 變量可見:一個變量的修改(assigned+store+write)馬上可以被其他線程觀察到。
  • 禁止指令重排序:initlized=true必須在init()之後執行。重點內容

volatile語義的實現:

  • 可見性只要強制要求read-load-use操作必須一起出現以及assign-store-write操作必須一起出現即可(一個volatile變量的assign操作要馬上同步回主內存,一個use操作需要先從主內存加載數據)。
  • 禁止指令重排序是通過內存屏障實現的,內存屏蔽是類似於lock add 0x0 esp之類的指令,通過在init()與initlized=true之間使用內存屏蔽指令,強制要求initlized=true必須在init()之後執行。

volatile的用處

  • 對共享變量x的修改不依賴與x的值,或只有一個線程修改x。
  • 變量不需要與其他狀態變量一起參與不變約束。

volatile+不可變類可以提供線程安全的變量訪問,因爲對於變量引用的變化可以立即被其他線程觀察到,而引用的對象狀態是不變的。

volatile+非原子性操作都不是線程安全的,因爲不滿足互斥性。
如if(x != 8) x = 9;或x++;之類的操作,因爲在load+use指令之後(將x存入寄存器),線程可能掛起,然後由其他線程對x的值進行變更,此時線程切回原線程就可能使用了歷史數據,而導致數據錯誤。

volatile最合適的使用方法就是第一個例子,用於單線程X設置一個狀態變量,利用其可見性以及禁止指令重排序的特點,將X的工作狀態及時的通知給其他線程。另外一個用法就是單例模式中的DCL方法(雙鎖檢測)。

volatile A instance = null;
A getInstance(){
  if(instance==null){
    synchronized(A.class){
        //在這裏使用了volatile的可見性,防止重複創建實例。
        if(instance == null){
            instance = new A();
        }
    }
  }
  return instance;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章