單例模式看了就夠了

1、餓漢式單例
它是在類加載的時候就立即初始化,並且創建單例對象
優點:沒有加任何的鎖、執行效率比較高,在用戶體驗上來說,比懶漢式更好
缺點:類加載的時候就初始化,不管你用還是不用,我都佔着空間,浪費了內存,有可能佔着茅坑不拉屎

絕對線程安全,在線程還沒出現以前就是實例化了,不可能存在訪問安全問題

注意代碼中的:readResolve方法及註釋內容

public class HungrySingleton {
    
    private static final HungrySingleton hungrySingleton = new HungrySingleton();

    private HungrySingleton() {}

    //提供全局訪問點
    public static HungrySingleton getInstance() {
        return hungrySingleton;
    }
    //防止通過序列化和反序列化的方式破壞單例
    private  Object readResolve(){
        return  INSTANCE;
    }
}

一上來就把單例對象創建出來了,要用的時候直接返回即可,這種可以說是單例模式中最簡單的一種實現方式。但是問題也比較明顯。單例在還沒有使用到的時候,初始化就已經完成了。也就是說,如果程序從頭到位都沒用使用這個單例的話,單例的對象還是會創建。這就造成了不必要的資源浪費。所以不推薦這種實現方式。

1.1 餓漢式靜態塊單例

public class HungryStaticSingleton {
    private static final HungryStaticSingleton hungrySingleton;
    static {
        hungrySingleton = new HungryStaticSingleton();
    }
    private HungryStaticSingleton(){}
    public static HungryStaticSingleton getInstance(){
        return  hungrySingleton;
    }
}

2.懶漢式單例(在外部需要使用的時候才進行實例化)

public class LazySimpleSingleton {
    //構造器私有化
    private LazySimpleSingleton(){}
    //靜態塊,公共內存區域
    private static LazySimpleSingleton lazy = null;

    public synchronized static LazySimpleSingleton getInstance(){
        if(lazy == null){
            lazy = new LazySimpleSingleton();
        }
        return lazy;
    }
}

3.Double CheckLock實現單例:DCL也就是雙重鎖判斷機制(由於JVM底層模型原因,偶爾會出問題,不建議使用):

public class LazyDoubleCheckSingleton {
    private volatile static LazyDoubleCheckSingleton lazy = null;

    private LazyDoubleCheckSingleton(){}
    public static LazyDoubleCheckSingleton getInstance(){
        if(lazy == null){
            synchronized (LazyDoubleCheckSingleton.class){
                if(lazy == null){
                    lazy = new LazyDoubleCheckSingleton();
                }
            }
        }
        return lazy;
    }
}

4.靜態內部類實現模式(線程安全,調用效率高,可以延時加載)

public class LazyInnerClassSingleton {
    //默認使用LazyInnerClassSingleton的時候,會先初始化內部類
    //如果沒使用的話,內部類是不加載的
    private LazyInnerClassSingleton(){
        //加if判斷,是爲了防止通過反射的方式獲取實例,而造成單例被破壞。也就是防止反射攻擊
        if(LazyHolder.LAZY != null){
            throw new RuntimeException("不允許創建多個實例");
        }
    }

    //每一個關鍵字都不是多餘的
    //static 是爲了使單例的空間共享
    //final 保證這個方法不會被重寫,重載
    public static final LazyInnerClassSingleton getInstance(){
        //在返回結果以前,一定會先加載內部類
        return LazyHolder.LAZY;
    }

    //默認不加載
    private static class LazyHolder{
        private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
    }
}

 

5.枚舉類(線程安全,調用效率高,不能延時加載,JDK層面天然的防止反射和反序列化調用)

JDK層面就不允許反射方式ENUM創建對象,可以查看jdk源碼

枚舉的單例模式使用我們的單例更加的優雅

public enum SingletonDemo5 {
     
    //枚舉元素本身就是單例
    INSTANCE;
     
    //添加自己需要的操作
    public void singletonOperation(){     
    }
}

6.註冊式單例

註冊式單例(容器式),保證線程內部的全局唯一,使用場景:多數據源動態切換。

public class ThreadLocalSingleton {
    private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance =
            new ThreadLocal<ThreadLocalSingleton>(){
                @Override
                protected ThreadLocalSingleton initialValue() {
                    return new ThreadLocalSingleton();
                }
            };

    private ThreadLocalSingleton(){}

    public static ThreadLocalSingleton getInstance(){
        return threadLocalInstance.get();
    }
}

單例模式總結:

1,私有化構造器

2,保證線程安全

3,延遲加載

4,防止序列化和反序列化破壞單例

5,防禦反射攻擊單例

缺點:1,沒有接口擴展困難;2,如果要擴展單例對象,只有修改代碼,沒有其他途徑

 

如何選用:

-單例對象 佔用資源少,不需要延時加載,枚舉 好於 餓漢

-單例對象 佔用資源多,需要延時加載,靜態內部類 好於 懶漢式

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