天天看點

設計模式--單例模式單例模式

單例模式

單例模式就是采取一定的方式保證在整個的軟體系統中,對某個類隻能存在一個執行個體對象,并且該類隻提供一個取得其對象執行個體的方法。

  • 單例模式保證了系統記憶體中該類隻存在一個對象,節省了系統資源,對于一些需要頻繁建立銷毀的對象,使用單例模式可以提高系統性能。
  • 當想執行個體化一個單例對象的時候,必須記住使用相應的擷取對象的方式,而不是用new。
  • 單例模式使用場景:需要頻繁的進行建立和銷毀的對象,建立對象時耗時過多或耗費資源過多(即重量級對象),但又經常喲用到的對象、工具類對象、頻繁通路資料庫和檔案的對象(比如資料源,session工廠等)。

1)餓漢式(靜态常量)

代碼:
package singleton.type01;

/**
 * @Author: [email protected]
 * 單例模式-- 餓漢式(靜态常量)
 * 優點:
 * 寫法簡單,就是在類裝載的時候就完成執行個體化,避免了線程同步問題
 * 缺點:
 * 在類裝載的時候完成執行個體化,沒有達到 lazy loading的效果。如果從開始
 * 至終從未使用過這個執行個體,則會造成記憶體的浪費
 * 這種方式基于classloder 機制,避免了多線程同步問題,不過 instance 在類裝載
 * 時就執行個體化,在單例模式中大多數都是調用 getInstance() 方法,但是導緻類裝載
 * 的原因有很多,是以不能确定有其他的方式(或者有其他的靜态方法)導緻類裝載,這
 * 時候初始化instance 就沒有達到lazy loading的效果
 * <p>
 * 結論:
 * 這種單例可以使用,可能造成記憶體浪費
 */
public class SingletonTest01 {
    public static void main(String[] args) {
        // 測試
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2); // true
    }
}

// 餓漢式(靜态變量)
class Singleton {
    // 1. 構造器私有化,外部不能new
    private Singleton() {

    }

    // 2. 在本類内部建立對象執行個體
    private final static Singleton instance = new Singleton();

    // 3.對外提供一個共有的靜态方法,傳回執行個體對象
    public static Singleton getInstance() {
        return instance;
    }
}
           

2)餓漢式(靜态代碼塊)

代碼:
package singleton.type02;

/**
 * @Author: [email protected]
 * 單例模式-- 餓漢式(靜态代碼塊)
 * 這種方式和上面的方式類似,隻不過将類執行個體化的過程放到靜态代碼塊中,也是在類裝載的
 * 時候,就執行靜态代碼塊中的代碼,初始化類的執行個體。優缺點和上面的一樣
 * 結論:
 * 這種單例可以使用,但是可能造成記憶體浪費
 */
public class SingletonTest02 {
    public static void main(String[] args) {
        // 測試
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2); // true
    }
}

// 餓漢式(靜态代碼塊)
class Singleton {
    // 1. 構造器私有化,外部不能new
    private Singleton() {

    }

    // 2. 在本類内部建立對象執行個體
    private static Singleton instance;

    static { // 在靜态代碼塊中,建立單例對象
        instance = new Singleton();
    }

    // 3.對外提供一個共有的靜态方法,傳回執行個體對象
    public static Singleton getInstance() {
        return instance;
    }
}
           

3)懶漢式(線程不安全)

代碼:
package singleton.type03;

/**
 * @Author: [email protected]
 * 懶漢式(線程不安全)
 * 起到了lazy loading的效果,但是隻能在單線程下使用。
 * 如果在多線程下,一個線程進入了if(singleton == null) 判斷語句塊,還未來得
 * 及往下執行,另一個線程也通過了這個判斷語句,這時便會産生多個執行個體。是以在多
 * 線程環境下不可使用這種方式
 * 結論:
 * 在實際開發中,不要使用這種方式
 */
public class SingletonTest03 {
    public static void main(String[] args) {
        // 測試
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2); // true
    }
}

// 懶漢式(線程不安全)
class Singleton {
    private static Singleton instance;

    private Singleton() {

    }

    // 提供一個靜态的公有方法,當使用到該方法時,才去建立instance
    // 即懶漢式
    public static Singleton getInstance() {
        if (instance == null) { // 判斷該對象沒有被建立則建立
            instance = new Singleton();
        }
        return instance;
    }
}
           

4)懶漢式(線程安全,同步方法)

代碼:
package singleton.typte04;

/**
 * @Author: [email protected]
 * 懶漢式(線程安全,同步方法)
 * 解決了線程不安全
 * 效率太低,每個線程在想獲得類的執行個體時候,執行getInstance() 方法都要執行同步。而
 * 其實這個方法隻執行一次執行個體化代碼就夠了,後面的想獲得該類執行個體,直接return即可。同
 * 步效率太低
 * 結論:
 * 在實際開發中,不推薦使用這種方法
 */
public class SingletonTest04 {
    public static void main(String[] args) {
        // 測試
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2); // true
    }
}

// 懶漢式(線程安全,同步方法)
class Singleton {
    private static Singleton instance;

    private Singleton() {

    }

    // 提供一個靜态的公有方法,加入同步處理的代碼,解決線程安全問題
    // 即懶漢式
    public static synchronized Singleton getInstance() {
        if (instance == null) { // 判斷該對象沒有被建立則建立
            instance = new Singleton();
        }
        return instance;
    }
}
           

5)懶漢式(線程安全,同步代碼塊)

代碼:
package singleton.type05;

/**
 * @Author: [email protected]
 * 懶漢式(線程安全,同步代碼塊)
 * 這種方式本意是想對第四種實作方式的改進,因為前者同步方法效率低,改為同步産生執行個體
 * 的代碼塊。
 * 但是這種同步并不能起到線程同步的作用。跟第三種實作方式遇到的情況一緻,假如一個
 * 線程進入了if(singleton == null) 判斷語句塊,還未來得及往下執行,另一個線程
 * 也通過了這個判斷語句,這時便會産生多個執行個體。
 * 結論:
 * 在實際的開發中,不能使用該方式
 */
public class SingletonTest05 {
    public static void main(String[] args) {
        // 測試
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2); // true
    }
}

// 懶漢式(線程安全,同步方法)
class Singleton {
    private static Singleton instance;

    private Singleton() {

    }

    // 提供一個靜态的公有方法,加入同步處理的代碼,解決線程安全問題
    // 即懶漢式
    public static  Singleton getInstance() {
        if (instance == null) { // 判斷該對象沒有被建立則建立
            synchronized (Singleton.class){ // 同步代碼塊
                instance = new Singleton();
            }
        }
        return instance;
    }
}
           

6)懶漢式(雙重檢查)

代碼:
package singleton.type06;

/**
 * @Author: [email protected]
 * 懶漢式(雙重檢查)
 * double-check 概念是多線程開發中經常使用到的,進行了倆次if(singleton==null)檢查
 * 可以保證線程安全
 * 這樣,執行個體化代碼隻執行一次,後面再次通路時,判斷if(singleton==null);直接return
 * 執行個體化對象,也避免的反複進行方法同步。
 * 線程安全,延遲加載,效率較高
 * 結論:
 * 在實際開發中,推薦使用這種單例模式
 */
public class SingletonTest06 {
    public static void main(String[] args) {
        // 測試
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2); // true
    }
}

// 懶漢式(線程安全,同步方法)
class Singleton {
    private static volatile Singleton instance;

    private Singleton() {

    }

    // 提供一個靜态的公有方法,加入雙重檢查代碼,解決線程安全問題,同時解決懶加載問題
    // 同時保證效率,推薦使用
    public static Singleton getInstance() {
        if (instance == null) { // 判斷該對象沒有被建立則建立
            synchronized (Singleton.class) { // 同步代碼塊
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
           

7)懶漢式(靜态内部類)

代碼:
package singleton.type07;

/**
 * @Author: [email protected]
 * 懶漢式(靜态内部類)
 * 這種方式采用了類裝載的機制來保證初始化執行個體時隻有一個線程。
 * 靜态内部類方式在Singleton類被加載的時并不會立即被執行個體化,而是在需要執行個體化時,
 * 調用getInstance方法,才會裝載 SingletonInstance 類,進而完成Singleton執行個體化
 * 類的靜态屬性隻會在第一次加載類的時候初始化,是以這裡,JVM幫助我們保證了線
 * 程的安全性,在類進行初始化時,别的線程無法進入的。
 * 優點:避免了線程不安全,利用靜态内部類特點實作延遲加載,效率高
 * 結論:推薦使用
 */
public class SingletonTest07 {
    public static void main(String[] args) {
        // 測試
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance1 == instance2); // true
    }
}

// 懶漢式(靜态内部類完成) 推薦使用
class Singleton {
    private static volatile Singleton instance;

    // 構造器私有化
    private Singleton() {

    }

    // 寫一個靜态内部類,該類中有一個靜态屬性 Singleton
    private static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton();
    }

    // 提供一個靜态的公有方法,直接傳回
    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }
}
           

懶漢式(枚舉)

代碼:
package singleton.type08;

/**
 * @Author: [email protected]
 * 懶漢式(枚舉)
 * 借助JDK1.5中添加的枚舉來實作單例模式,不僅能避免多線程同步問題,
 * 而且還能防止反序列化重新建立新的對象
 * 這種方式是Effective Java 作者Josh Bloch 提倡的方法
 * 結論:推薦使用
 */
public class SingletonTest08 {
    public static void main(String[] args) {
        // 測試
        Singleton instance1 = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;
        System.out.println(instance1 == instance2); // true
        instance1.sayOK();
    }
}

// 枚舉
enum Singleton {
    INSTANCE;

    public void sayOK() {
        System.out.println("ok");
    }
}
           

繼續閱讀