Java設計模式之單例模式,完整到不敢想象

單例模式

做用

保證一個類只有一個實例,而且提供一個訪問該實例的全局訪問入口java

單例模式的經常使用

1.Windows的任務管理器
2.Windows的回收站,也是一個單例應用
3.項目中的讀取配置文件的對象
4.數據庫的鏈接池
5.Servlet中的Application Servlet
6.Spring中的Bean默認也是單例的
7.SpringMVC Struts中的控制器程序員

單例模式的優勢

1.因爲單例模式只生成一個實例,減小了系統給的性能開銷,當一個對象須要產生時,當時消耗的資源較多。那麼產生對象時構建的方式就能夠經過單例去構建。
2.單例模式存在全局訪問點,因此能夠優化共享資源訪問。數據庫

常見的單例模式的構建方法:

1.餓漢式:線程安全 調用率高 可是不能延遲加載
2.懶漢式:線程安全 調用率不高 可是能夠延遲加載
3.雙重檢測(double check )
4.靜態內部類(線程安全 能夠延遲加載)
5.枚舉單例 線程安全 不能夠延遲加載安全


代碼案例展現

餓漢式bash

/*** 餓漢式:* 類只要被加載就會被加載全局變量,因此餓漢式,會被及時加載。(沒有懶加載 )* 而且存在自然的線程安全問題。* @author 碼歌老薛* @date 建立時間 猴年馬月* @version 1.0*/public class SingleHungry {//提供靜態的全局變量 做爲訪問該類實例的入口private static SingleHungry sh = new SingleHungry();/*** 構造器私有 沒法建立對象*/private SingleHungry(){}/*** 對外提供get方法獲取 該類的實例* @return*/public static SingleHungry getInstance(){return sh;}}複製代碼

 

懶漢式jvm

/** 
 * 懶漢式: 
*      全局變量初始化放到了實例化方法中,延遲產生對象。 
 *      可是當多個線程統一訪問時,有可能出現線程不安全的狀況。須要優化。 
 * @author 碼歌老薛 
 * @date 建立時間 猴年馬月 
 * @version 1.0 
*/  
public class SingleLazy implements Serializable{  
   //提供靜態的全局變量 做爲訪問該類實例的入口 可是這裏不當即加載  
    private static SingleLazy sh = null;  
      
     
    /** 
     * 構造器私有 沒法建立對象 
     */  
    private SingleLazy(){   
       System.out.println("構造函數被調用了");  
   }  
      
   /** 
     * 對外提供get方法獲取 該類的實例 
    * @return 
    * @throws InterruptedException  
     */  
    public static synchronized SingleLazy getInstance() {  
       if(sh==null){  
            sh = new SingleLazy();  
       }  
        return sh;  
         
    }  
        
} 

複製代碼
上海尚學堂java培訓 shsxt.com複製代碼

雙重檢測函數

/** 
 * 懶漢式: 
 *      全局變量初始化放到了實例化方法中,延遲產生對象。 
 *      可是當多個線程統一訪問時,有可能出現線程不安全的狀況。須要優化。 
 * @author 碼歌老薛 
 * @date 建立時間 猴年馬月 
 * @version 1.0 
 */  
public class SingleLazy4 {  
    //提供靜態的全局變量 做爲訪問該類實例的入口 可是這裏不當即加載  
    private volatile  static SingleLazy4 sh = null;  
      
      
    /** 
     * 構造器私有 沒法建立對象 
     */  
    private SingleLazy4(){  
        System.out.println("被調用了");  
    }  
      
    /** 
     * 雙重校驗鎖式(也有人把雙重校驗鎖式和懶漢式歸爲一類)分別在代碼鎖先後進行判空校驗 
     * ,雙重校驗鎖式是線程安全的。然而,在JDK1.5之前,DCL是不穩定的,有時也可能建立多個實例, 
     * 在1.5之後開始提供volatile關鍵字修飾變量來達到穩定效果。 
     * 雙重校驗鎖DCL(double checked locking) 
     * @return 
     * @throws InterruptedException  
     */  
    public static SingleLazy4 getInstance() {  
        if(sh == null){  
            synchronized(SingleLazy4.class){  
                if(sh == null){  
                    sh = new SingleLazy4();  
                   //return singleton;    //有人提議在此處進行一次返回  
                }  
                //return singleton;    //也有人提議在此處進行一次返回  
            }  
        }  
        return sh;  
    }  
}  

上海尚學堂Java培訓 shsxt.com 獲取更多java學習資料複製代碼

靜態內部類性能

/** 
 *靜態內部類 
 *
 * @author 碼歌老薛
 * @date 建立時間 猴年馬月 
 * @version 1.0 
 */  
public class SingleInner {  
      
    /** 
     *靜態內部類式和餓漢式同樣,一樣利用了ClassLoader的機制保證了線程安全; 
     *不一樣的是,餓漢式在Singleton類被加載時(從代碼段3-2的Class.forName可見) 
     *就建立了一個實例對象,而靜態內部類即便Singleton類被加載也不會建立單例對象, 
     *除非調用裏面的getInstance()方法。由於當Singleton類被加載時 
     *,其靜態內部類SingletonHolder沒有被主動使用。只有當調用getInstance方法時, 
     *纔會裝載SingletonHolder類,從而實例化單例對象。 
 
    這樣,經過靜態內部類的方法就實現了lazy loading,很好地將懶漢式和餓漢式結合起來, 
    既實現延遲加載,保證系統性能,也能保證線程安全  
     */  
    private static class SingleInnerHolder{  
        private static SingleInner instance = new SingleInner();  
    }  
      
    private SingleInner(){  
        System.out.println("我被調用了");  
   }  
    public static SingleInner getInstance(){  
        return SingleInnerHolder.instance;  
    }  
}  複製代碼


枚舉單例學習


/** 
 * jvm提供底層保證  
 * 不可能出現序列化、反射產生對象的漏洞 可是不能作到延遲加載 
在外部,能夠經過EnumSingleton.INSTANCE.work()來調用work方法。默認的枚舉實例的建立是線程安全的 
、,可是實例內的各類方法則須要程序員來保證線程安全。 
總的來講,使用枚舉單例模式,有三個好處: 
 1.實例的建立線程安全,確保單例。2.防止被反射建立多個實例。3.沒有序列化的問題。 
 * @author 碼歌老薛 
 * @date 建立時間 猴年馬月 
 * @version 1.0 
 */  
public enum SingleEnum {  
    //實例化對象  
    INSTANCE;  
      
    /** 
     * 對象須要執行的功能 
     */  
    void getInstance(){  
          
    }  
}  

上海尚學堂java培訓 shsxt.com複製代碼

反射/序列化 獲取對象 以及防止方式優化


import java.io.ObjectStreamException;  
import java.io.Serializable;  
  
/** 
 * 懶漢式: 
 *      全局變量初始化放到了實例化方法中,延遲產生對象。 
 *      可是當多個線程統一訪問時,有可能出現線程不安全的狀況。須要優化。 
 * @author 碼歌老薛 
 * @date 建立時間 猴年馬月 
 * @version 1.0 
 */  
public class SingleLazy implements Serializable{  
    //提供靜態的全局變量 做爲訪問該類實例的入口 可是這裏不當即加載  
   private static SingleLazy sh = null;  
      
      
    /** 
     * 構造器私有 沒法建立對象 
     */  
    private SingleLazy(){  
       if(sh!=null){  
            throw new RuntimeException();  
        }  
        System.out.println("構造函數被調用了");  
    }  
      
    /** 
     * 對外提供get方法獲取 該類的實例 
     * @return 
     * @throws InterruptedException  
     */  
    public static synchronized SingleLazy getInstance() {  
        if(sh==null){  
            sh = new SingleLazy();  
        }  
        return sh;  
          
    }  
      
    private Object readResolve()throws ObjectStreamException{  
        return sh;  
    }  
      
}  

複製代碼
上海尚學堂java培訓 shsxt.com複製代碼

用法總結:

一、懶漢式效率是最低的。
二、佔用資源少 不須要延時加載 枚舉優於 餓漢式
三、佔用資源比較多 須要延時加載 靜態內部類 優於 懶漢式

更多Java技術文章歡迎閱讀上海尚學堂Java培訓,免費試學和線上公開課培訓課程等你學習。

相關文章
相關標籤/搜索