單例模式是GOF設計模式中最簡單的一個,也是最常見的一個,當我們遇到某個類在全局中只應該存在一個實例的時候就需要使用單例,單例有很多種寫法,不同的寫法有不用好處和使用情景,下面做一個簡要的歸納。
常見的幾種單例模式寫法
- 餓漢模式
public class Singleton {
/**
* 優(yōu)點:沒有線程安全問題,簡單
* 缺點:提前初始化會延長類加載器加載類的時間;如果不使用會浪費內存空間; 不能傳遞參數(shù)
*/
private static final Singleton instance = new Singleton();
private Singleton(){};
public static Singleton getInstance(){
return instance;
}
}
餓漢式的原理其實是基于classloder機制來避免了多線程的同步問題 。
- 經(jīng)典模式
public class Singleton {
/**
* 優(yōu)點:易于使用,延遲初始化,節(jié)約資源(又稱懶漢式寫法)
* 缺點:當有多個線程同時調用getInstance()會出現(xiàn)線程安全問題
*/
private static final Singleton instance;
private Singleton(){};
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
平時開發(fā)如果沒有線程安全的需求這種寫法就可以滿足了
- 解決經(jīng)典模式的線程安全問題
public class Singleton {
/**
* 優(yōu)點:易于使用,延遲初始化,節(jié)約資源 同時保證了線程安全
* 缺點:每次調用都會走synchronized,十分影響性能
*/
private static final Singleton instance;
private Singleton(){};
public synchronized static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
這種寫法對性能影響比較大,如果調用不是很頻繁還可以接受,一般不推薦這種寫法,下面有更好的寫法。
- 綜合性能和線程安全模式
public class Singleton {
/**
* 優(yōu)點:易于使用,延遲初始化,節(jié)約資源 同時保證了線程安全
* 缺點:每次調用都會走synchronized,性能問題
*/
private volatile static final Singleton instance;
private Singleton(){};
public static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
一般來講這種寫法是很推薦的寫法,當然這里的instance最好用volatile來修飾,如果我們在new SingleTon中做了很多事情(Singleton中有很多成員對象需要初始化賦值的時候)比如:
1.instance對象分配內存;
2.初始化對象成員變量賦值;
3.將instance指向分配的內存空間 (此時instance才不為null));
這些操作并非原子操作,java虛擬機編譯時可能會對指令重新排序,這樣2、3兩步?jīng)]法保證先后順序,這樣有的線程就可能就會拿到為null的instance,程序就會出問題,利用volatile的禁止指令重排序優(yōu)化特性,就可以解決這個問題。
- 解決餓漢模式提前初始化的寫法
public class Singleton{
/**
* 優(yōu)點:解決線程安全,延遲初始化(四大名著之 Effective Java推薦寫法)
* 缺點:好像沒有~_~
*/
private Singleton(){}
public static Singleton getInstance () {
return Holder.SINGLE_TON;
}
private static class Holder{
private static final Singleton SINGLE_TON = new Singleton();
}
}
好了以上五種就是java中常見的五種單例寫法,最后兩種推薦使用,一般常見第四種,第五種以后要多多嘗試,畢竟谷歌大神推薦_.