關於單例的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打斷,取到不完整的引用。

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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM