1. 程式人生 > >設計模式之建立型(1)——單例模式

設計模式之建立型(1)——單例模式

定義:保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。

三種寫法:

(1)懶漢式

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

    public static SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }
}

優點:使用時才建立,節約資源

缺點: 執行緒不安全,因為多執行緒環境下,new SingletonLazy()可能會執行多次。

改進1:給getInstance()方法加鎖。

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

    public synchronized static SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }
}

問題:多執行緒環境下,雖然能保證單例,但是每個執行緒進入getInstance() 方法時,都會掛起其他的執行緒,即便SingletonLazy已經被例項化了,方法也不是立即返回,而synchronzized是有時間代價的,所以效率不高。

改進2:雙重檢查

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

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

優點:多執行緒環境下

1. 執行getInstance()方法時,不需要阻塞,如果SingletonDoubleCheck已經被建立,則方法迅速返回物件例項。這是第一層檢查的的作用。

2. 只在SingletonDoubleCheck未被初始化的時候才加鎖,這是比“改進1”效率高的地方。第二層檢查的作用是保證單例。

問題:new SingletonDoubleCheck()不是一個“原子操作”,就是說這個操作需要n步才能完成,這裡涉及一個JVM的話題——指令重排序,這就可能導致先修改了instance的值,再執行真正的構造方法。所以,還是有可能出現一個執行緒未出這個語句塊時,另一個執行緒進入。(筆者對這個地方,還不太能解釋清楚,先做個標記,後面完善)

改進3:加volatile

public class SingletonDoubleCheck {
    private volatile static SingletonDoubleCheck instance = null;
    private SingletonDoubleCheck () {}

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

 

(2)餓漢式

public class SingletonHungry {
    private static SingletonHungry instance = new SingletonHungry();
    private SingletonHungry () {}

    public static SingletonHungry getInstance() {
        return instance;
    }
}

優點:執行緒安全,由JVM load機制保證

缺點:過早浪費資源 

(3) 靜態內部類式(推薦使用)

public class Singleton {
    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
    
    private Singleton() {}

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

優點:在使用時才建立例項物件,JVM機制。