單例模式: 確保某一個類只有一個實例,並且自行實例化並向整個系統提供這個實例。bash
避免產生多個對象消耗過多的資源,或者某種類型的對象只有一個。函數
public static class Singleton {
private static final instance Singleton = new Singleton();
private Singleton() { // 私有構造函數
}
public static Singleton getInstance() {
return instance;
}
}
複製代碼
instance是靜態成員變量,在聲明時就被實例化。優化
public static class Singleton {
private static final instance Singleton = null;
private Singleton() { // 私有構造函數
}
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton()
}
return instance;
}
}
複製代碼
instance只是在初次調用時初始化,synchronized,每次調用getInstance()方法都會進行同步,這樣會消耗沒必要要的資源,這也是懶漢模式最大的問題。ui
public static class Singleton {
private static final instance Singleton = null;
private Singleton() { // 私有構造函數
}
public static Singleton getInstance() {
if(instance == null) {
synchronized(Singleton.class) {
if(instance == null) {
instance = new Singleton()
}
}
}
return instance;
}
}
複製代碼
DCL有兩次判空,第一次判空,是爲了不沒必要要的同步;第二次判空,是爲了初次初始化時,只初始化一次。
spa
instance = new Singletone(); 這行代碼最終會編譯成多條彙編指令,它大體作了三件事:線程
DCL失效狀況:
因爲Java編譯器容許處理器亂序執行,以及JDK1.5以前JMM(Java Memory Model)中Cache、寄存器到內存回寫順序的規定,上面第二和第三的順序是沒法保證的。也就是說有可能JVM會爲新的Singleton實例分配空間,而後直接賦值給instance成員,而後再去初始化這個Singleton實例,這樣就可能出錯了。咱們以A、B兩個線程爲例:
code
在JDK1.5以後,SUN公司調整了JVM,增長了volatile關鍵字,只需將instance的定義改爲private volatile static Singleton instance = null就能夠保存instance對象每次都是從主內存總讀取。對象
public static class Singleton {
private Singleton() { // 私有構造函數
}
private static class SingletonHolder {
private static final instance Singleton = new Singleton();
}
public static synchronized Singleton getInstance() {
return SingletonHolder.instance;
}
}
複製代碼
當第一次加載Singleton類時,並不會初始化,只有當第一次調用getInstance()方法,會致使虛擬機加載SingletonHolder類,此時纔是初始化。利用的Java類加載機制接口
不一樣的類裝載器,可能會致使產生多個單例對象:內存
public static Class getClass(String className) throws ClassNotFoundException {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl == null) {
cl = Singleton.class.getClassLoader();
}
return cl.loadClass(className);
}
複製代碼
若是Singleton實現了Serializable接口,反序列化時可能產生多個實例對象:
private Object readResolve() { // 重寫readResolve方法
return instance;
}
複製代碼