1. 程式人生 > >設計模式:單例設計模式

設計模式:單例設計模式

內部 建議 null 需要 餓漢 重排序 let sin 網站

單例設計模式

由於某些類創建對象可能會耗費內存和花費時間。一般將這種類設計為單例設計模式會比較好。

1.對象在內存中只有一個,減少了內存的開銷

2.可以控制對象的創建時刻

單例模式的特點:

1.單例的類在整個JVM中只有一個實例

2.需要提供一個全局訪問點(1.公開的靜態變量,2.公開的靜態方法)

1.餓漢式:

特點:類加載的時候就創建了實例

//餓漢式
/*
 *1.類能被創建且只有一個實例
 * 2.提供一個全局的訪問點
 * */
public class Singleton {
    //創建唯一的實例對象
    private static Singleton singletonInstance = new
Singleton(); //修改默認的構造器為私有的,屏蔽外部的new方法 private Singleton() {} //全局的訪問點 public static Singleton getInstance() { return singletonInstance; } }

2.懶漢式

特點:在需要創建實例的時候才調用方法創建實例對象

class Singleton2 {
    //保存唯一的實例對象
    private static Singleton2 singletonInstance;
    //修改默認的構造器為私有的,屏蔽外部的new方法
private Singleton2() {} //全局的訪問點 public static Singleton2 getInstance() { if(singletonInstance == null) { singletonInstance = new Singleton2(); } return singletonInstance; } }

3.加鎖的懶漢式

由於一般懶漢式不能保證線程安全。所以需要在方法加鎖保證線程安全

//加鎖的懶漢式
class Singleton3 {
    
//保存唯一的實例對象 private static Singleton3 singletonInstance; //修改默認的構造器為私有的,屏蔽外部的new方法 private Singleton3() {} //全局的訪問點 //給創建實例的方法加鎖。防止在多線程條件下線程問題 public synchronized static Singleton3 getInstance() { if(singletonInstance == null) { singletonInstance = new Singleton3(); } return singletonInstance; } }

4.雙重驗證的懶漢式

由於枷鎖的懶漢式對整個方法加了鎖,會導致每次調用創建實例方法都會需要進行等待,但是如果實例已經創建了,應該是不想繼續等待的。所以應該只在判斷實例是否創建的地方加鎖即可

class Singleton4 {
    //保存唯一的實例對象
    private static Singleton4 singletonInstance;
    //修改默認的構造器為私有的,屏蔽外部的new方法
    private Singleton4() {}
    //全局的訪問點
    public  static Singleton4 getInstance() {
        if(singletonInstance == null) {
            //獲取單例類的鎖
            synchronized (Singleton4.class) {
                if(singletonInstance == null) {
                    singletonInstance = new Singleton4();
                }
            }
        }
        return singletonInstance;
    }
}

5.加上volatile關鍵字防止重排序的雙重驗證懶漢式

創建一個對象可以分為3步:

技術分享圖片

雖然重排序不會影響單線程的執行結果,但是由於判斷的條件是instance == null,當分配了內存以後,其他線程來到判斷的地方,instance不為空,所以直接將引用指向實例對象。但是實例對象還沒有初始化,就會出現問題。

所以需要放防止重排序

class Singleton5 {
    //volatile防止重排序
    private volatile static Singleton5 singletonInstance;
    //修改默認的構造器為私有的,屏蔽外部的new方法
    private Singleton5() {}
    //全局的訪問點
    //給創建實例的方法加鎖。防止在多線程條件下線程問題
    public  static Singleton5 getInstance() {
        if(singletonInstance == null) {
            //獲取單例類的鎖
            synchronized (Singleton5.class) {
                if(singletonInstance == null) {
                    singletonInstance = new Singleton5();
                }
            }
        }
        return singletonInstance;
    }
}

6.靜態內部類的方式:通過類加載過程的線程安全來保證創建單例實例的線程安全

class Singleton6 {
    //修改默認的構造器為私有的,屏蔽外部的new方法
    private Singleton6() {}
    //靜態內部類保存唯一的實例對象
    private static class InnerInstanceHolder{
        private static Singleton6 singletonInstance = new Singleton6();
    }
    //類加載是線程安全的,所以使用內部類持有唯一實例是線程安全的,且效率比較高
    public static Singleton6 getInstance() {
        return InnerInstanceHolder.singletonInstance;
    }
}

7.枚舉方式

//枚舉方式
public enum Singleton {
        INSTANCE;
        public void method() {
        }
}

暫時還沒想懂枚舉方式的特點。列出參考網站下次研究:

為什麽我墻裂建議大家使用枚舉來實現單例

圖片參考來源:https://blog.csdn.net/qq_36109365/article/details/78090096

設計模式:單例設計模式