1. 程式人生 > 其它 >單例模式(Singleton Pattern)

單例模式(Singleton Pattern)

單例模式(Singleton Pattern)

定義

確保一個類只有一個例項,並提供一個全域性訪問點。

類圖

懶漢式

所謂的懶漢式就是延遲物件例項化

程式碼

public class Singleton {
    private static Singleton singleton;

    private Singleton(){
        System.out.println("我是單例模式獨一無二的物件");
    };

    //物件只有在呼叫了該方法後才進行例項化
    public static Singleton getInstance() {
        if(singleton==null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

單執行緒測試

public class SingletonTest {
    public static void main(String[] args) {
        Singleton singleton =Singleton.getInstance();
        singleton = Singleton.getInstance();
        singleton = Singleton.getInstance();
        singleton = Singleton.getInstance();
    }
}

多執行緒測試

public class SingletonTest {
    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run() {
                Singleton singleton =Singleton.getInstance();
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                Singleton singleton =Singleton.getInstance();
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                Singleton singleton =Singleton.getInstance();
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                Singleton singleton =Singleton.getInstance();
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                Singleton singleton =Singleton.getInstance();
            }
        }.start();
    }
}

測試結果:

出現結果分析

簡單改進

//修改getInstance()方法,給它加synchronized 關鍵字
 public synchronized static Singleton getInstance() {
        if(singleton==null){
            singleton = new Singleton();
        }
        return singleton;
    }

但這樣子真的好嘛?仔細想想,好像確實對效能的影響很大,因為我們只在第一次執行這個方法時,才真正需要同步,換句話說,一旦設定好singleton變數,就不再需要同步這個方法了,之後呼叫這個方法,同步都是一種累贅。

改善多執行緒的方法

  1. 如果getInstance()的效能對應用程式不是很關鍵,那就不需要改了!
    同步getInstance()的方法既簡單又有效。但你必須知道,同步一個方法可能造成程式執行效率下降100倍。應此,如果將getInstance()的程式使用在頻繁執行的地方,你可能就得重新考慮了!
  2. 使用“急切”(餓漢式)建立例項,而不用延遲例項化(懶漢式)的做法。
    如果程式總是建立並使用單例例項,或者在建立和執行時方面的負擔不太繁重,可以使用此方法
public class Singleton {
    private static Singleton singleton =new Singleton();
    //在靜態初始化器中建立單例。這段程式碼保證了執行緒安全。

    private Singleton(){
        System.out.println("我是單例模式獨一無二的物件");
    };

    public static Singleton getInstance() {
        return singleton;//已經有例項了,直接使用它
    }
}
  1. 用“雙重檢索加鎖”,在getInstance()中減少使用同步
    如果效能是你關注的重點,那麼這個做法可以幫你大大減少getInstance()的時間耗費。
public class Singleton {
    private volatile static Singleton singleton;

    private Singleton(){
        System.out.println("我是單例模式獨一無二的物件");
    };

    public static Singleton getInstance() {  
        if(singleton==null){//檢查是否已經建立
            synchronized(singleton){//未建立,則進入同步
                if(singleton==null){//再檢查一次,如果仍是null,才建立例項
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

補充:
volatile關鍵詞確保singleton變數被初始化成Singleton例項時,多執行緒正確地處理singleton變數

總結

懶漢式:延遲了例項化,在單執行緒模式下,能達到單例模式的效果,但在多執行緒環境時,就會出現意想不到的bug,簡單的在getInstance()方法上加synchronized,會降低多執行緒的執行效率,可以利用“雙重檢索加鎖”的方法,提高效率
餓漢式:在靜態初始化器中建立單例,如果程式總是建立並使用單例例項,或者在建立和執行時方面的負擔不太繁重,可以使用此方法,它將不再使用延遲例項化的方法建立單例,依賴JVM在載入這個類時,馬上建立此唯一單例,保證在任何執行緒訪問是singleton變數時,一定先建立此例項!