單例模式(Singleton Pattern)作為Java設(shè)計模式之一,保證一個類僅有一個實(shí)例,并提供一個訪問它的全局訪問點(diǎn)。節(jié)省系統(tǒng)資源污抬,也適用于需要全局實(shí)例的場景。本文中介紹的四種單例模式皆為線程安全的寫法绳军。
1. 雙重檢查
public class MySingletonDoubleCheck {
private static volatile MySingletonDoubleCheck singleton;
private MySingletonDoubleCheck(){
}
private static MySingletonDoubleCheck getInstence(){
if (singleton == null){
synchronized (MySingletonDoubleCheck.class){
if (singleton == null){
singleton = new MySingletonDoubleCheck();
return singleton;
}
}
}
return singleton;
}
}
主要思想
定義了私有的靜態(tài)成員變量印机,volatile關(guān)鍵字可以禁止指令重排序,同時寫了一個私有的空的無參構(gòu)造方法防止強(qiáng)行使用new實(shí)例出一個變量门驾。synchronized (MySingletonDoubleCheck.class)
保證了當(dāng)同時有兩個線程想要實(shí)例化singleton變量時射赛,只會讓第一個線程實(shí)例化出變量,等到第二個線程進(jìn)入同步代碼塊時singleton變量已經(jīng)不為null了奶是。
volatile關(guān)鍵字的作用
volatile關(guān)鍵字的作用:在new出一個新對象的過程中楣责,計算機(jī)其實(shí)將其分成了3個步驟,第一步首先給singleton分配內(nèi)存聂沙,第二步隨后調(diào)用構(gòu)造方法初始化秆麸,第三步最后將singleton對象指向分配的內(nèi)存空間,此時singleton才不為null及汉【谌ぃ看起來順理成章,但是計算機(jī)為了提高效率坷随,在不影響結(jié)果的情況下房铭,可能會改變這三個步驟的順序,也就是說温眉,可能會先執(zhí)行第三步缸匪,再執(zhí)行第二步。注意芍殖,執(zhí)行完第三步之后豪嗽,singleton變量就已經(jīng)不是null了,但實(shí)際上它還沒有完成初始化,此時如果有第二個線程也想要得到單例對象龟梦,在第一次判斷singleton是否為null時就將尚未初始化的singleton返回隐锭,此時返回的singleton是不完整的。
優(yōu)點(diǎn)
延遲加載计贰,效率高钦睡。
缺點(diǎn)
代碼稍稍復(fù)雜了一點(diǎn),需要對鎖有一定理解躁倒,同時volatile也會影響一點(diǎn)點(diǎn)性能荞怒,但無傷大雅。
2. 餓漢式(靜態(tài)常量)
public class Singleton {
private static final Singleton INSTANCE = new SingletonSample();
private Singleton(){
}
public static Singleton getInstance(){
return INSTANCE;
}
}
主要思想
采用靜態(tài)常量的方式實(shí)現(xiàn)單例秧秉,十分簡單褐桌,但依舊不能忘了寫一個私有無參構(gòu)造方法。
優(yōu)點(diǎn)
寫法簡單象迎。
缺點(diǎn)
在類裝載時就被實(shí)例化荧嵌,沒有達(dá)到懶加載(Lazy Loadind)的效果,有可能造成資源浪費(fèi)砾淌,比如從始至終都未使用過這個實(shí)例啦撮。
3. 靜態(tài)內(nèi)部類
public class SingletonStatic {
private SingletonStatic(){
}
private static class SingletonInstance{
private static final SingletonStatic SINGLETON_STATIC = new SingletonStatic();
}
public static SingletonStatic getInstance(){
return SingletonInstance.SINGLETON_STATIC;
}
}
主要思想
采用靜態(tài)內(nèi)部類的形式,思想與餓漢式類似汪厨,但靜態(tài)內(nèi)部類在類加載時不會被實(shí)例化赃春,只有在第一次調(diào)用getInstance()方法時才會加載,類的靜態(tài)屬性只會在第一次加載類的時候初始化劫乱,所以JVM保證了線程的安全性织中,在類進(jìn)行初始化時,別的線程無法干擾要拂。
優(yōu)點(diǎn)
延遲加載抠璃,效率高。
缺點(diǎn)
沒有什么缺點(diǎn)脱惰。
4. 枚舉+接口
- 以下為自定義接口:
public interface MySingleton {
void doSomething();
}
- 利用枚舉實(shí)現(xiàn)單例模式:
public enum Singleton implements MySingleton {
INSTANCE {
@Override
public void doSomething() {
System.out.println("It is my singleton");
}
};
public static Singleton getInstance(){
return Singleton.INSTANCE;
}
}
主要思想
借助JDK1.5中添加的枚舉來實(shí)現(xiàn)單例模式搏嗡。
優(yōu)點(diǎn)
不僅能避免多線程同步問題,而且還能防止反序列化重新創(chuàng)建新的對象拉一。
缺點(diǎn)
代碼較其他三種比較怪異采盒。