單例模式是我們編程中最常用的設(shè)計模式,小記練習一下:
1、餓漢法:
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
這是最簡單的方法,在類引用的時候就會實例化。不能延遲加載,某種意義上會增加系統(tǒng)的負載。
2、懶漢法:
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
在餓漢法的基礎(chǔ)上增加了延遲加載,只有調(diào)用getInstance的方法才會實例化對象,在單線程中沒有問題,但在多線程中有可能導致多次實例化。
3、多線程安全型:
public class Singleton {
private static volatile Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
return instance;
}
}
在instance對象加上volatile關(guān)鍵字,保證其對所有的線程可見性,并禁止其對指令重排序優(yōu)化。在判斷null和new的部分進行加鎖。但是每次調(diào)用的時候,都要通過synchronize鎖機制排隊等待,會有效率方面的問題。
4、多線程安全性兼顧效率(雙重檢查鎖):
public class Singleton {
private static volatile Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
在synchronized關(guān)鍵字外層再加一個判斷null,只有為null后才會進入方法內(nèi),可以減少絕大部分的鎖操作。
==注意:volatile關(guān)鍵字在JDK 1.5之前無法完全保證指令優(yōu)化重排序,所以在JDK1.5之前雙重校驗是有問題的==
5、靜態(tài)內(nèi)部類
public class Singleton {
private Singleton(){}
private static class Holder{
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return Holder.instance;
}
}
將創(chuàng)建的過程放在靜態(tài)內(nèi)部類中,靜態(tài)內(nèi)部類只有被調(diào)用的時候才會加載,所以符合延遲加載。靜態(tài)內(nèi)部類只會被加載一次,所以是線程安全的。
以上所有方式,在序列化的時候都需要額外的工作(Serializable、transient、readResolve()),否則在反序列化的時候都會創(chuàng)建一個新的實例。
6、枚舉方式:
public enum Singleton {
INSTACE;
private String name;
public String getName() {
return name;
}
}
使用枚舉方式實現(xiàn)的單例模式,自帶線程安全和防止反射強行調(diào)用構(gòu)造器,還提供了自動序列化的機制,防止反序列化的時候創(chuàng)建新的對象。