確保某一個類只有一個實例,並且自行實例化並向整個系統提供這個實例java
確保某一個類有且只有一個對象的場景,避免產生多個對象消耗過多的資源,或者某種類型的對象只應該有且只有一個。例如要訪問IO和數據庫等資源,這時候就能夠考慮使用單列模式。數據庫
classDiagram Singleton <.. Client class Singleton{ +getInstanc()Singleton -Singleton() }
public class Singleton{
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
複製代碼
這種方式在類加載的時候就自行實例化,避免了多線程同步的問題。安全
public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
複製代碼
這種方式的優勢是隻有在第一次使用的時候纔會被實例化,能夠看到 getInstance() 添加了 synchronized 修飾,它是一個同步方法,這是爲了在多線程的狀況下保證單列對象的惟一性,若是隻在單線程使用能夠不加。也是由於添加了 synchronized 致使每次調用getInstance()都須要進行同步,致使沒必要要的同步開銷,因此不建議使用這種寫法。markdown
public class Singleton{
private volatile static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
複製代碼
這種方式相比上面的避免了不必的同步,同時也能作到線程安全。能夠看到有兩次判空,第一次試爲了不沒必要要的同步,第二次是爲了確保單列對象的惟一性。這裏仍是用了 volatile 關鍵字,若是不用就有可能出現DCL 失效的問題。多線程
是什麼致使DCL 失效的?咱們接着往下看。函數
假設A線程執行到 instance = new Singleton() 這行代碼,這行代碼最終會被編譯成多條指令,大體作如下3鍵事情:性能
可是因爲Java 編譯器容許處理器亂序執行,這時候就有可能出現執行順序爲1-3-2,當A線程執行完3還未執行2的時候,B線程取走了instance ,在使用時就會出錯,這就是DCL 失效問題。而使用volatile 關鍵字修飾能夠禁止進行指令重排序,全部能夠有效的避免這個問題。固然使用volatile 也會對性能有一點影響,但考慮程序的正確性,這點犧牲是值得的。spa
public class Singleton{
private Singleton(){}
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
}
複製代碼
由於靜態內部類不會由於外部類的加載而加載,靜態內部類加載不須要依附於外部類,但在加載靜態內部類時必定會加載外部類。線程
所以使用這種方式能夠確保線程安全,也能保證單列對象的惟一性,同時也延遲了單列的實例化,也不會有性能影響。因此這是最推薦的使用方式。code
public enmu Singleton{
INSTANCE;
public void doSomething(){
}
}
複製代碼
枚舉默認是線程安全的並且反序列化也不會致使從新建立對象,保證單列對象的惟一性。若是以前4種模式要杜絕反序列化時從新生成對象,那麼必須加入 readResolve() 函數。
public class Singleton implements Serializable{
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
private Object readResolve() throws ObjectStreamException{
return instance;
}
}
複製代碼
以上介紹了單列的5種實現方式,並不是所有。就我的而言,不考慮懶加載的狀況下使用 餓漢模式 便可,不然建議使用 靜態內部類模式,若是須要考慮反序列化的狀況能夠考慮 枚舉模式 ,枚舉模式由於可讀性不太好,因此通常用的比較少。