單例模式【創建型模式】

文章優先發布在個人博客。 https://www.xdx97.com/article/699258099130695680

所謂單例模式,就是對某個類只能存在一個對象實例,並且該類只提供一個獲取其對象實例的方法(靜態方法)

單例模式一共有八種方式

1、餓漢式 (靜態常量)

2、餓漢式 (靜態代碼塊)

3、懶漢式 (靜態方法內部判斷,線程不安全)

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

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

6、雙重檢查

7、靜態內部類

8、枚舉


1、餓漢式

1-1:餓漢式(靜態常量)

1、構建

    1、構造器私有化(防止 new)
    2、類的內部創建對象
    3、向外暴露一個靜態的公共方法(getInstance)

2、代碼演示

public class Main {
    // 測試
    public static void main(String[] args) {
        Singleton one = Singleton.getInstance();
        Singleton two = Singleton.getInstance();
        System.out.println(one == two);   // true
        System.out.println("one.hashCode() = " + one.hashCode());  // 兩個hashcode相同
        System.out.println("two.hashCode() = " + two.hashCode());
    }

}

class Singleton {
    // 1、私有化,防止 new
    private Singleton(){}
    // 2、本類內部創建實例
    private final static Singleton instance = new Singleton();
    // 3、提供一個公有的靜態方法,返回實例對象
    public static Singleton getInstance(){
        return instance;
    }
}

3、分析

3-1:優缺點
  • 優點:寫法比較簡單,就是在類加載的時候完成實例化。避免了線程同步問題。
  • 缺點:在類加載的時候就完成實例化,沒有達到懶加載的效果。如果從始至終從未使用過這個實例,則會造成內存浪費。

1-2:餓漢式(靜態代碼塊)

1、代碼演示 (測試和上面的測試一樣,就不贅述了)

class Singleton {
    // 1、私有化,防止 new
    private Singleton(){}
    // 2、創建變量
    private static Singleton instance;
    // 3、靜態創建對象
    static {
        instance = new Singleton();
    }
    // 4、提供一個公有的靜態方法,返回實例對象
    public static Singleton getInstance(){
        return instance;
    }
}

2、優缺點,和上面一樣不再贅述。


2、懶漢式

1、懶漢式(線程不安全的)

1、代碼演示

class Singleton {
    // 1、私有化,防止 new
    private Singleton(){}
    // 2、創建變量
    private static Singleton instance;
    // 3、提供一個公有的靜態方法,返回實例對象
    public static Singleton getInstance(){
        if (instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

2、優缺點

  • 優點:起到了懶加載的作用
  • 缺點:線程不安全。(一個線程進入 if 語句後,還沒來得及往下執行,另一個線程也通過了判斷語句,這時候就會產生多個實例 )

2、懶漢式(使用 synchronized 同步方法, 保證線程安全)

1、代碼演示

class Singleton {
    // 1、私有化,防止 new
    private Singleton(){}
    // 2、創建變量
    private static Singleton instance;
    // 3、提供一個同步靜態方法,返回實例對象。解決線程安全問題
    public synchronized static Singleton getInstance(){
        if (instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

2、優缺點

  • 優點:解決了線程安全的問題。
  • 缺點:方法使用了同步,效率太低了。

3、懶漢式 (線程不安全,同步代碼塊)

不可以這樣寫,它連最起碼的線程安全都無法保證。

1、代碼演示

class Singleton {
  
    private Singleton(){}

    private static Singleton instance;

    public  static Singleton getInstance(){
        if (instance == null){
            synchronized(Singleton.class){
                instance = new Singleton();
            }
        }
        return instance;
    }
}

3、雙重檢查

1、代碼演示

class Singleton {

    private Singleton(){}

    private volatile static Singleton instance;

    public  static Singleton getInstance(){
        if (instance == null){
	    // 1
            synchronized(Singleton.class){
		// 2
                if (instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

說明:volatile可以保證可見性、有序性,還有禁止指令重排序。比如當我們線程A已經執行到了 2,線程B執行到了 1。這個時候線程A執行結束創建了對象,B再進來了發現對象已經創建了(volatile的作用)就不再創建了。而之後的線程直接在第一個if就被擋住了。

2、優缺點

  • 優點:做到了懶加載、解決了線程安全問題和效率問題。

7、靜態內部類

1、代碼演示

class Singleton {

    private Singleton(){}

    private static class SingletonInstance{
        private static final Singleton INSTANCE = new Singleton();
    }

    public  static Singleton getInstance(){
        return SingletonInstance.INSTANCE;
    }
}

說明:

  • 外部類裝載的時候,內部類不會進行裝載的。
  • 當我們調用getInstance的時候內部類會進行裝載,裝載的過程是線程安全的。

2、優缺點

  • 優點:避免線程安全問題,滿足了延遲加載,效率高。

8、枚舉

public class Main {
    public static void main(String[] args) {
        Singleton one = Singleton.INSTANCE;
        Singleton two = Singleton.INSTANCE;
        System.out.println(one == two);
        System.out.println(one.hashCode());
        System.out.println(two.hashCode());
    }
}
enum  Singleton {
    INSTANCE;
    public void fun(){
        System.out.println("fun...");
    }
}

2、優缺點

  • 優點:避免線程安全問題,而且還能防止反序列化重新創建新的對象。

9、其它

1、JDK裏面那裏使用到了單例模式?

    Runtime裏面就用到了單例模式的餓漢模式,它把構造方法私有化了,然後直接使用靜態常量創建對象。


2、單例模式注意事項

  • 單例保證了系統內存中該類只存在一個對象,節省了系統資源,對於一些需要頻繁創建銷燬的對象,使用單例模式可以提高系統的性能。
  • 當想實例化一個單例類的時候,必須記住使用相應的獲取對象的方法,而不是使用new
  • 單例模式使用場景:需要頻繁的進行創建和銷燬的對象、創建對象時消耗過多或消費資源過多(重量級對象),但又經常使用到的對象、工具類對象、頻繁訪問數據庫或文件的對象(比如數據源、session工程等)




如果對你有幫助了的話,可以關注我的公衆號,一起成長噢
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章