單例模式

單例模式:某個類只能有一個實例,提供一個全局的訪問點。spring

單例模式
●核心做用:保證一個類只有一個實例,而且提供一一個訪問該實例的全局訪問點。
●常見應用場景:
一Windows的Task Manager (任務管理器)就是很典型的單例模式
windows的Recycle Bin (回收站)也是典型的單例應用。在整個系統運行過程當中,回收站一直維護着僅有的一個實例。
項目中,讀取配置文件的類,通常也只有一 個對象。 沒有必要每次使用配置文件數據,每次new-個對象去讀取。
網站的計數器,通常也是 採用單例模式實現,不然難以同步。
-應用程序的日誌應用 , -般都何用單例模式實現,這通常是因爲共享的日誌文件一直處於打開狀態,由於只能有一個實例去操做
, 不然內容很差追加。
數據庫鏈接池的設計-般也是採用單例模式 ,由於數據庫鏈接是一種數據庫資源。
-操做系統的文件系統 ,也是大的單例模式實現的具體例子,-個操做系統只能有一個文件系統。
Application也是單例的典型應用( Servlet編程中會涉及到)
-在Spring中 ,每一個Bean默認就是單例的,這樣作的優勢是Spring容器能夠管理
-在servlet編程中 ,每一個Servlet也是單例
-在spring MVC框架/struts1框架中,控制器對象也是單例

●單例模式的優勢:
-因爲單例模式只生成一 個實例,減小了系統性能開銷,當-個對象的產生須要
比較多的資源時,如讀取配置、產生其餘依賴對象時 ,則能夠經過在應用啓動
時直接產生一個單例對象,而後永久駐留內存的方式來解決
單例模式能夠在系統設置全局的訪問點,優化環共享資源訪問,例如能夠設計
一個單例類,負責全部數據表的映射處理
●常見的五種單例模式實現方式:
主要:
餓漢式(線程安全,調用效率高。可是,不能延時加載。)
//類初始化時,當即加載這個對象(沒有延時加載的優點)。加載類時,自然的是線程安全的!
//方法沒有同步,調用效率高!
public class SingletonDemo1 {
 
 //類初始化時,當即加載這個對象(沒有延時加載的優點)。加載類時,自然的是線程安全的!
 private static SingletonDemo1 instance = new SingletonDemo1(); 
 
 private SingletonDemo1(){
 }
 
 //方法沒有同步,調用效率高!
 public static SingletonDemo1  getInstance(){
  return instance;
 }
 
}
懶漢式(線程安全,調用效率不高。可是,能夠延時加載。)
lazy load 延遲加載,懶加載,真正用的時候纔去加載
資源利用率高了,可是每次調用getInstance都要同步,併發效率低了
public class SingletonDemo2 {
 
 //類初始化時,不初始化這個對象(延時加載,真正用的時候再建立)。
 private static SingletonDemo2 instance; 
 
 private SingletonDemo2(){ //私有化構造器
 }
 
 //方法同步,調用效率低!
 public static  synchronized SingletonDemo2  getInstance(){
  if(instance==null){
   instance = new SingletonDemo2();
  }
  return instance;
 }
 
}
其餘:
雙重檢測鎖式(因爲JVM底層內部模型緣由,偶爾會出問題。不建議使用)
靜態內部類式(線程安全,調用效率高。可是,能夠延時加載)
枚舉單例(線程安全,調用效率高,不能延時加載)
1.測試餓漢式單例模式
public class SingletonDemo1 {
 //類初始化時,當即加載這個對象(沒有延時加載的優點)。加載類時,自然的是線程安全的!
 private static SingletonDemo1 instance = new SingletonDemo1(); 
 private SingletonDemo1(){
 }
 //方法沒有同步,調用效率高!
 public static SingletonDemo1  getInstance(){
  return instance;
 }
 
}
2.測試懶漢式單例模式
public class SingletonDemo2 {
 //類初始化時,不初始化這個對象(延時加載,真正用的時候再建立)。
 private static SingletonDemo2 instance;   
 private SingletonDemo2(){ //私有化構造器
 }
 //方法同步,調用效率低!
 public static  synchronized SingletonDemo2  getInstance(){
  if(instance==null){
   instance = new SingletonDemo2();
  }
  return instance;
 }
 
}
3. 雙重檢查鎖實現單例模式
public class SingletonDemo3 {
  private static SingletonDemo3 instance = null;
  public static SingletonDemo3 getInstance() {
    if (instance == null) {
      SingletonDemo3 sc;
      synchronized (SingletonDemo3.class) {
        sc = instance;
        if (sc == null) {
          synchronized (SingletonDemo3.class) {
            if(sc == null) {
              sc = new SingletonDemo3();
            }
          }
          instance = sc;
        }
      }
    }
    return instance;
  }
  private SingletonDemo3() {
  }
   
}
4.測試靜態內部類實現單例模式
 * 這種方式:線程安全,調用效率高,而且實現了延時加載!
public class SingletonDemo4 {
 private static class SingletonClassInstance {
  private static final SingletonDemo4 instance = new SingletonDemo4();
 }
 
 private SingletonDemo4(){
 }
 
 //方法沒有同步,調用效率高!
 public static SingletonDemo4  getInstance(){
  return SingletonClassInstance.instance;
 }
 
}
 5.測試枚舉式實現單例模式(沒有延時加載)
public enum SingletonDemo5 {
 //這個枚舉元素,自己就是單例對象!
 INSTANCE;
 //添加本身須要的操做!
 public void singletonOperation(){
 }
 
 
}

 * 測試懶漢式單例模式(如何防止反射和反序列化漏洞)
public class SingletonDemo6 implements Serializable {
 //類初始化時,不初始化這個對象(延時加載,真正用的時候再建立)。
 private static SingletonDemo6 instance; 
 
 private SingletonDemo6(){ //私有化構造器
  if(instance!=null){
   throw new RuntimeException();
  }
 }
 
 //方法同步,調用效率低!
 public static  synchronized SingletonDemo6  getInstance(){
  if(instance==null){
   instance = new SingletonDemo6();
  }
  return instance;
 }
 
 //反序列化時,若是定義了readResolve()則直接返回此方法指定的對象。而不須要單獨再建立新對象!
 private Object readResolve() throws ObjectStreamException {
  return instance;
 }
 
}
測試反射和反序列化破解單例模式
public class Client2 {
 
 public static void main(String[] args) throws Exception {
  SingletonDemo6 s1 = SingletonDemo6.getInstance();
  SingletonDemo6 s2 = SingletonDemo6.getInstance();
  
  System.out.println(s1);
  System.out.println(s2);
  
  //經過反射的方式直接調用私有構造器
  Class<SingletonDemo6> clazz = (Class<SingletonDemo6>) Class.forName("com.bjsxt.singleton.SingletonDemo6");
  Constructor<SingletonDemo6> c = clazz.getDeclaredConstructor(null);
  c.setAccessible(true);
  SingletonDemo6  s3 = c.newInstance();
  SingletonDemo6  s4 = c.newInstance();
  System.out.println(s3);
  System.out.println(s4);
 }
}
 //經過反序列化的方式構造多個對象
  FileOutputStream fos = new FileOutputStream("d:/a.txt");
  ObjectOutputStream oos = new ObjectOutputStream(fos);
  oos.writeObject(s1);
  oos.close();
  fos.close();
  
  ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt"));
  SingletonDemo6 s3 =  (SingletonDemo6) ois.readObject();
  System.out.println(s3);
  
  
 }
}
測試枚舉式實現單例模式
public class Client {
 
 public static void main(String[] args) {
  SingletonDemo4 s1 = SingletonDemo4.getInstance();
  SingletonDemo4 s2 = SingletonDemo4.getInstance();
  
  System.out.println(s1);
  System.out.println(s2);
  
  System.out.println(SingletonDemo5.INSTANCE==SingletonDemo5.INSTANCE);
  
  
 }
}
 * 測試多線程環境下五種建立單例模式的效率
public class Client3 {
 
 public static void main(String[] args) throws Exception {
  
  long start = System.currentTimeMillis();
  int threadNum = 10;
  final CountDownLatch  countDownLatch = new CountDownLatch(threadNum);
  
  for(int i=0;i<threadNum;i++){
   new Thread(new Runnable() {
    @Override
    public void run() {
     
     for(int i=0;i<1000000;i++){
//      Object o = SingletonDemo4.getInstance();  //測試靜態內部類
      Object o = SingletonDemo5.INSTANCE;   //測試枚舉類
     }
     
     countDownLatch.countDown();
    }
   }).start();
  }
  
  countDownLatch.await(); //main線程阻塞,直到計數器變爲0,纔會繼續往下執行!
  
  long end = System.currentTimeMillis();
  System.out.println("總耗時:"+(end-start));
 }
}
CountDownLatch -同步輔助類,在完成一組正在其餘線程中執行的操做以前,它容許一 個或多個線程一直等待。 countDown()當前線程調此方法,則計數減一 (建議放在finally裏執行) await(,調用此方法會直阻塞當前線程,直到計時器的值爲0)
相關文章
相關標籤/搜索