設計模式學習筆記2-單例模式
1 單例模式
1.1 懶漢式(執行緒不安全)
public class Singleton {
private static Singleton instance;
private Sinleton() {
}
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
懶漢式單例模式是在得到例項的時候進行物件的初始化,但這個形式的單例不是執行緒安全的。
1.2 懶漢式(執行緒安全)
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
這個形式的單例模式使用synchronized
1.3 餓漢式(執行緒安全)
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
餓漢式單例模式是在類載入的時候就例項化的,避免了多執行緒的同步,利用空間換時間,但這種方式類一載入就機型instance的初始化,沒有達到懶載入
1.4 雙重校驗
public class Singleton {
private volatile static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if(instance == null) {
synchronized(Singleton.class) {
if(singleton == null) {
singleton = new Singleton();
}
}
}
}
}
雙重檢驗是利用兩次校驗對instance進行判斷instance是否被例項化,如果沒有別例項化則進行同步操作,在同步操作裡面再進行判斷(防止第一次判斷時有幾個執行緒同時進入),如果沒有例項化則進行例項化,這樣的好處就是可以大大減少同步的用時,大大節省時間。
1.5 靜態內部類方式
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton() {
}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
這裡利用了類記載機制來保證初始化instance時只有一個執行緒,這種方式在Singleton類被裝載也不一定會被初始化,因為:SingletonHolder類沒有被制動使用,只有通過顯示的呼叫getInstance方法時,才會顯示裝載SingletonHolder類,從而例項化instance(這就保證了懶載入)。
1.6 列舉方式
enum Singleton{
INSTANCE;
public void otherMethods(){
System.out.println("Something");
}
}
優點:自由序列化,執行緒安全,保證單例;
使用方法:直接Singleton.INSTANCE;即得到一個例項。
enum是通過繼承了Enum類實現的,enum結構不能夠作為子類繼承其他類,但是可以用來實現介面。
enum有且僅有private的構造器,防止外部的額外構造,這恰好和單例模式吻合,也為保證單例性做了一個鋪墊。這裡展開說下這個private構造器,如果我們不去手寫構造器,則會有一個預設的空參構造器,我們也可以通過給列舉變數參量來實現類的初始化。
對於序列化和反序列化,因為每一個列舉型別和列舉變數在JVM中都是唯一的,即Java在序列化和反序列化列舉時做了特殊的規定,列舉的writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法是被編譯器禁用的,因此也不存在實現序列化介面後呼叫readObject會破壞單例的問題。
在序列化中會通過反射呼叫無參建構函式建立一個新的物件。
外加:雙重檢測的變形(防止序列化破壞單例模式)
public class Singleton {
private volatile static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if(instance == null) {
synchronized(Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
}
private Object readResolve() {
return instance;
}
}
這個在Singleton中添加了一個readResovle();在該方法中指定要返回的物件的生成策略,就可以防止單例被破壞。