關於單例的DCL方式分析

public class Singleton {   
    
    /**  
     * 單例對象實例  
     */  
    private volatile static Singleton instance = null;   
    
    public static Singleton getInstance() {   
        if (instance == null) {   
            synchronized (Singleton.class) {   
                if (instance == null) {   
                    instance = new Singleton();   
                }   
            }   
        }   
        return instance;   
    }   
}

  這是一個典型的DCL單例,其中volatile在以前已經說過了,能夠保證不管什麼時候讀取這個變量,都是讀到內存中最新的值,不管什麼時候寫這個變量,均可以當即寫到內存中。多線程

  可是並無這麼簡單,在沒有見volatile修飾instance時,在編譯後,編譯器會自動把第二個判斷刪除,由於編譯器判斷這個程序在執行過程當中,這個值是不會改變的,編譯器不考慮多線程的狀況。加了volatile,是告訴編譯器,這個變量隨時有可能會被其餘線程改變,這樣編譯器就不會把這兩個判斷優化成一個判斷了。優化

  同時,volatile的變量,能夠保證對該變量的操做具備原子性,典型的例子是long和double型變量,一般須要分兩步讀寫一個double變量,volatile修飾的double能夠保證對一個double變量的操做的兩部分不會被多線程插入。以及對引用類型賦值的,new一個實例的過程不會被其餘線程插入(new在編譯指令中是分紅幾步執行的,防止這幾步在執行過程當中被其餘線程取這個變量值,取到一個不完整的實例)。能夠簡單的理解爲對volatile變量的set和get方法加上了synchronized關鍵字,在new的過程當中,整個都處在set中,因此不會被其餘線程的get打斷,取到不完整的引用。spa

  原子性針對一個long或者double或者一個引用類型,對於引用類型,原子性是指在new實例的過程當中,不會被其餘線程取到,即不會被其餘線程取到一個不完整的實例。這種原子性能夠理解爲new的過程處於一個synchronize段的set方法中,只有set結束才能夠被get到,即new的整個過程都是處於set中的。也能夠理解爲指令重排序,禁止把new過程的指令與把引用賦值給變量的語句重排序,賦值只發生在new結束以後。線程

  再次,在第二次判斷if (instance == null)的時候,會再次取一次instance,再次取這個instance已是全部線程最新的,每次修改的引用都會實時反映到主內存中。code

相關文章
相關標籤/搜索