歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Java設計模式-單例模式

Java設計模式-單例模式

日期:2017/3/1 9:09:17   编辑:Linux編程

相信大家去面試的時候,經常被問到單例模式的有關問題吧,今天我們就來好好總結一下
一 懶漢式

public class Singleton {  
    private static Singleton instance = null;  
    private Singleton() {}  
    public static Singleton getInstance() {  

        if (instance == null) {   
            instance = new Singleton();   
        }  
        return instance;  
    }  
}

相信大家對這段代碼再熟悉不過了,懶漢式的意思就是每次在調用getInstance方法時,才去new一個Singleton的實例,但是很明顯,按上面這樣寫有問題啊,線程不同步啊,那麼怎麼辦呢

public class Singleton {  
    private static Singleton instance = null;  
    private Singleton() {}  
    public static synchronized  Singleton getInstance() {  

        if (instance == null) {   
            instance = new Singleton();   
        }  
        return instance;  
    }  
}  

在getInstance方法之前加上synchronized不就解決了,但是這樣,每次調用getInstance都會同步,而我們只是希望在第一次new Singleton()的時候同步即可,一旦instance不為空了,就不用再同步了,那麼怎麼做呢

public 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;  
    }  
} 

在代碼塊利用鎖,並且要加上雙重檢測,因為兩個線程很可能同時執行到第一個判空的地方,可能判斷都為空,就都會進入下面的代碼裡,就會生成兩個對象,所以要加上雙重鎖定,但是這樣就沒問題了嗎,當然不是,我們都知道java內存模型中有無序寫的機制,instance = new Singleton(); 這行其實做了兩個事情:1、調用構造方法,創建了一個實例。2、把這個實例賦值給instance這個實例變量。可問題就是,這兩步jvm是不保證順序的。也就是說。可能在調用構造方法之前,instance已經被設置為非空了
比如順序被重排了,先保證instance 不為null,後面才調用構造函數初始化,這個時候另外一個線程判斷instance 不為空,直接返回instance ,這樣就會有問題,那麼怎麼解決這個問題呢,我們知道 volatile關鍵字可以在instance 賦值的時候,禁止重排序

public class Singleton {
    private volatile static Singleton instance; //聲明成 volatile
    private Singleton (){}

    public static Singleton getSingleton() {
        if (instance == null) {                         
            synchronized (Singleton.class) {
                if (instance == null) {       
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

這樣這個問題就解決了,但是是不是覺得很累,下面給出一種完美的解決方法,利用靜態內部類

public class Singleton {  

    private static class SingletonHolder{  
        private static Singleton instance = new Singleton();  
    }  

    private Singleton() {  
    }  

    //獲取單例對象實例  
    public static Singleton getInstance() {  
        return SingletonHolder.instance;  
    }  
}  

我們知道,靜態內部類只有在getInstance裡第一次被調用的時候才加載,實現了懶加載,而且加載過程是線程安全的,所以一般用這種方法來實現懶漢式單例子

二 餓漢式

//餓漢式  
public class Singleton {  
    private static Singleton instance = new Singleton();       
    private Singleton() {}  
    public static Singleton getInstance() {  
        return instance;  
    }  
}  

在類加載的時候就new好對象,這樣也不存在線程安全問題,但是餓漢式也有問題,如果構造的單例很大,構造完又遲遲不使用,會導致資源浪費。

總結:推薦使用懶漢式靜態內部類的方式實現單例模式,好了,設計模式-單例模式就總結到這裡,如有問題,歡迎指正,謝謝。

Copyright © Linux教程網 All Rights Reserved