volatile關鍵字

volatile關鍵字

  • volatile關鍵字是輕量級的 synchronized

當變量被聲明爲 volatile的時候,在對volatile變量進行寫操做時候,彙編指令會插入一個 Lock前綴指令,這個指令會引起兩件事情。java

  1. 將當前處理器緩存行寫回到系統內存。
  2. 這個寫回內存的操做會是其餘CPU裏緩存了該內存地址的數據無效。
  • 緩存一致性協議緩存

    在多處理器下,爲了保證各個處理器緩存是一致的,就會實現緩存一致性協議,每一個處理器經過嗅探在總線上傳播的數據來檢查本身緩存的值是否是過時了。當處理器發現本身緩存行對應的內存地址被修改,就會將當前處理器的緩存行置爲無效狀態。app

volatile關鍵字的內存語義

volatile的特性

  • 可見性,對於一個volatile變量的讀,任意線程老是能看到對這個volatile變量最後的寫入。
  • 原子性,對於任何一個volatile變量的讀/寫具備原子性(針對long和double),可是對於 volatile++不具備原子性。

volatile 的寫-讀 構建的 happens-before關係

從 jdk5 開始,volatile變量的寫-讀能夠實現線程之間的通訊。
從內存語義的角度來看,volatile寫-讀與鎖的釋放-獲取具備相同的內存效果。volatile寫和鎖的釋放具備相同的內存語義,volatile讀與鎖的獲取有相同的內存語義。線程

volatile 內存語義的實現

爲了實現volatile內存語義,JMM將限制編譯器重排序和處理器重排序。JMM針對編譯器制定的volatile重排序規則表。code

可否重排序 第二個操做
第一個操做 普通讀寫 volatile讀 volatile寫
普通讀寫 NO
volatile讀 NO NO NO
volatile寫 NO NO

總結爲一下3點:對象

  • 當第二個操做是 volatile寫的時候,不能重排序,保證了第一個操做不能重排序到volatile寫以後
  • 當第一個操做是 volatile讀的時候,不能重排序,保證了第二個操做不能重排序到volatile讀以前
  • 當第一個操做是volatile寫,第二個操做是volatile讀的時候,不能重排序。

爲了實現volatile的內存語義,編譯器在生成字節碼的時候,會在指令序列中插入內存屏障來禁止特定類型的處理器重排序。排序

StoreLoad屏障內存

JSR-133 爲何要加強volatile的內存語義

舊的java內存模型中,雖然不容許volatile變量之間重排序,可是容許 volatile變量與普通變量重排序。
所以在舊的內存模型中,volatile的寫-讀沒有鎖的釋放-獲取所具備的語義。爲了實現一種比鎖更輕量級的線程之間的通訊機制。JSR-133中加強了volatile的內存語義,嚴格限制編譯器和處理器對volatile變量與普通變量的重排序。get

雙重檢查鎖定與延遲初始化

public class InstanceFactory {
    private static Instance instance;
    
    public static Instance getInstance(){
        if(instance==null){
            synchronized(InstanceFactory.class){
                if(instance==null){
                    instance =new Instance();
                }
            }
        }
        return instance;
    }
    static class Instance{
        //some code
    }
}

這個代碼在 看上去是正確,有可能會獲得一個爲徹底初始化成功的對象。編譯器

建立對象的代碼能夠分爲3個部分:

memory = allocate(); //1.分配內存
ctorInstance(memory);//2.初始化對象
instance = memory;   //3.設置instance的內存地址

這個過程能夠重排序爲:

memory = allocate(); //1.分配內存
instance = memory;   //3.設置instance的內存地址

ctorInstance(memory);//2.初始化對象

因此當第二個判斷 instance == null ,當判斷對象不爲空的時候,對對象進行訪問可能會訪問到一個爲徹底初始化成功的對象。

解決方法:1.禁止 操做二、3重排序
可使用volatile變量修飾instance 禁止操做二、3重排序。 這個方案須要在jdk1.5或者更高的版本才行

public class InstanceFactory {
    private static volatile Instance instance;
    
    public static Instance getInstance(){
        if(instance==null){
            synchronized(InstanceFactory.class){
                if(instance==null){
                    instance =new Instance();
                }
            }
        }
        return instance;
    }
    static class Instance{
        //some code
    }
}
相關文章
相關標籤/搜索