設計模式之單例模式 Singleton實現


餓漢式

  •           餓漢式單例模式,static變量會在類裝載時初始化,此時也不會涉及多個線程對象訪問該對象的問題。虛擬機保證只會裝載一次該類,肯定不會發生並發訪問的問題,

                     因此可以省略synchronized關鍵字

  •           問題:如果只是加載本類,而不是要調用getInstance(),甚至永遠沒有調用,則會造成資源浪費

         

public class SingletonDemo1 {
    //類初始化時,立即加載這個對象(沒有延時加載的優勢),,記載類時,天然的是線程安全的
    private static SingletonDemo1 instance = new SingletonDemo1(); 
    private SingletonDemo1(){
        
    };
    
    //方法沒有同步,效率高
    public static SingletonDemo1 getInstance(){
        return instance;
    }
    
}

懶漢式

  • Lazy Load,延時加載,真正用到的時候再加載
  • 每次都得使用同步,synchronized,效率就底下了。
public class SingletonDemo2 {
    //類初始化時,不初始化這個對象,(延時加載,真正用到的時候再創建)
    private static SingletonDemo2 instance ; 
    private SingletonDemo2(){
        
    };
    
    //方法同步,效率低
    public static synchronized SingletonDemo2 getInstance(){
        
        
        if(instance==null){
            instance= new SingletonDemo2();
        }
        return instance;
    }
    
}

雙重檢測鎖

  • 雙重檢測判斷
  • 使用volatile關鍵字,保證可見性
public class Singleton {
    private volatile static Singleton sSingleton;

    private Singleton() {
    }

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

 靜態內部類

  • 外部類沒有static關鍵字,所以不會立即加載
  • 只有調用getInstance()時才會加載靜態內部類,線程安全。instance前面加了static final關鍵字,因此保證了內存中只有這樣一個實例存在,且只能賦值一次,final保證線程安全
  • 具有並發高效調用和延遲加載的雙重優點
public class SingletonDemo3 {
    
    private static class SingletonClassInstance{
        private static final SingletonDemo3 instance = new SingletonDemo3() ; 
    }
    
    private SingletonDemo3(){
        
    }
    
    public static SingletonDemo3 getInstance(){
        return SingletonClassInstance.instance;
    }
    
}

枚舉實現

  • 實現簡單
  • 枚舉本身就是單例模式
public enum SingletonDemo4 {
    
    //枚舉元素本身就是單例對象
    INSTANCE;
    
    //添加自己需要的操作
    public void singletonOperation(){
        
    }
}

如何防止反射和反序列化漏洞

  • 反射可以破解上面幾種(不包含枚舉式)實現方式  --------------------------可以在構造方法中手動拋出異常控制
  • 反序列化可以破解上面幾種(不包含枚舉式)實現方式

            -------可以通過定義readResolve()防止獲得不同對象。
            --------反序列化時,如果對象所在類定義了readResolve(),定義返回哪個對象,實際是一種回調。

public class SingletonDemo5 {
    //類初始化時,不初始化這個對象,(延時加載,真正用到的時候再創建)
        private static SingletonDemo5 instance ; 
        private SingletonDemo5(){
            if(instance !=null){
                throw new RuntimeException();
            }
        }
        
        //方法同步,效率低
        public static synchronized SingletonDemo5 getInstance(){
            if(instance==null){
                instance= new SingletonDemo5();
            }
            return instance;
        }
        
        //反序列化時,如果定義了readResolve(),則直接返回此方法指定的對象,而不需要單獨創建新的對象
        private Object readResolve(){
            return instance;
        }
}

五種單例模式比較

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

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM