1. 程式人生 > >設計模式學習筆記2-單例模式

設計模式學習筆記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

關鍵字對getInstance()進行同步操作,所以在得到例項的時候是執行緒安全的。

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();在該方法中指定要返回的物件的生成策略,就可以防止單例被破壞。