單例模式

為什么需要單例模式

1.在日常web系統(tǒng)開發(fā)中,如果使用spring框架作為ioc容器,默認(rèn)情況下所有的bean都是以單例模式構(gòu)建的,如我們寫的service,dao,controller這樣的類,其實(shí)一般都是與線程無關(guān)的,也就是無狀態(tài)的,我們沒有必要在每一次用戶請求時(shí)都去新生成一個(gè)controller,一個(gè)service。。。spring為全局生成一個(gè)唯一實(shí)例,所有請求都復(fù)用就可以了,節(jié)約內(nèi)存。
2.如果某各類本身的實(shí)例化過程需要涉及很多的計(jì)算,很多的資源初始化開銷,我們肯定要讓其在全局只初始化一次,節(jié)約性能。
3.當(dāng)我們的代碼是有狀態(tài)的,涉及到并發(fā)時(shí),我們一定會(huì)加鎖進(jìn)行并發(fā)訪問控制來保證線程安全性。

public class ThreadSafeTest {

    private static int VARIBLE = 1;

    public synchronized void add(){
       VARIBLE += 1;
    }
}

以上代碼用synchronized來保證了VARIBLE增加的安全性,但是如果ThreadSafeTest類在系統(tǒng)中存在大于一個(gè)的實(shí)例,VARIBLE變量是類靜態(tài)屬性,而synchronized鎖住的是實(shí)例對象本身,這個(gè)add方法將不再安全,以上代碼要變成線程安全有幾種改法:1.VARIBLE設(shè)置為原子變量,2.add方法的鎖改為類鎖,3.把ThreadSafeTest類改為單例。

n種實(shí)現(xiàn)方法

1.最基本的非線程安全的懶漢式

 public class Singleton {  
      private static Singleton instance;  
      private Singleton (){}   
      public static Singleton getInstance() {  
         if (instance == null) {   //這里先判斷后執(zhí)行非原子操作,線程不安全
            instance = new Singleton();  
         }  
      return instance;  
      }  
 } 

2.線程安全的懶漢式

public class Singleton {  
      private static Singleton instance;  
      private Singleton (){}   
      //synchronized控制了線程安全性,但是直接鎖住整個(gè)方法,性能將會(huì)嚴(yán)重下降
      public static synchronized  Singleton getInstance() {  
         if (instance == null) {
            instance = new Singleton();  
         }  
      return instance;  
      }  
 } 

3.餓漢式,線程安全

 public class Singleton {  
     //類裝載時(shí)進(jìn)行實(shí)例化,如果實(shí)例化過程特別耗費(fèi)資源,將會(huì)導(dǎo)致啟動(dòng)緩慢
     private static Singleton instance = new Singleton();  
     private Singleton (){}
     public static Singleton getInstance() {  
           return instance;  
     }  
 }  

4.餓漢式變種

 public class Singleton {  
     private static Singleton instance;  
     {  //沒啥好說的,和3一毛一樣
         instance = new Singleton();  
     }
     private Singleton (){}
     public static Singleton getInstance() {  
           return instance;  
     }  
 }  

5.靜態(tài)內(nèi)部類實(shí)現(xiàn)

  public class Singleton {  
      //static修飾該類,為類級別內(nèi)部類,調(diào)用時(shí)才進(jìn)行裝在,初始化
      private static class SingletonHolder {  
       //jvm保證只會(huì)初始化一次INSTANCE屬性
      private static final Singleton INSTANCE = new Singleton();  
      }  
      private Singleton (){}
      public static final Singleton getInstance() {  
          //調(diào)用SingletonHolder.INSTANCE時(shí),jvm才會(huì)裝載初始化SingletonHolder類,保證了線程安全。
          return SingletonHolder.INSTANCE;  
      }  
  }  

6.雙重鎖檢測

 public class Singleton {  
      //volatile關(guān)鍵字保證singleton被修改后內(nèi)存可見性
      private volatile static Singleton singleton;  
      private Singleton (){}   
      //不對方法進(jìn)行加鎖
      public static Singleton getSingleton() {  
      //先判斷singleton是否為空,volatile關(guān)鍵字保證為空可見性,只有前幾次請求會(huì)走到下面的分支
      if (singleton == null) {  
          //對類進(jìn)行加鎖,并準(zhǔn)備對singleton進(jìn)行初始化
          synchronized (Singleton.class) {  
          //獲取到鎖后需要再次判斷singleton是否已經(jīng)被實(shí)例化過了,因?yàn)樵谧约号抨?duì)獲得鎖的過程中很有可能別的線程已經(jīng)得到鎖并實(shí)例化過了
          if (singleton == null) {  
              singleton = new Singleton();  
          }  
         }  
     }  
     return singleton;  
     }  
 }  

7.枚舉實(shí)現(xiàn)(effectiv java中推薦的高級方式,牛逼閃閃)

 public enum Singleton {  
     INSTANCE;  

     //以下是Singleton類本身具有的各個(gè)方法具體邏輯
     public void whateverMethod1() {  
     }  
     // ...其他的更多的自身方法
 }  

這種方式確實(shí)叼,腦洞大開啊。。。。它不僅能避免多線程同步問題,而且還能防止反序列化重新創(chuàng)建新的對象,而且實(shí)現(xiàn)起來非常非常簡單,但是很多人都覺得枚舉里面有太多的業(yè)務(wù)邏輯很奇怪~~~確實(shí)。。

反序列化和不同的類加載器可能破壞單例模式的解決方式

1.反序列化。

public class Singleton implements java.io.Serializable {     
    //這里只是以餓漢式舉例,其他任何方式都可以
     public static Singleton INSTANCE = new Singleton();     
        
     protected Singleton() {     
          
     }     
     //readResolve返回的對象替換反序列化創(chuàng)建的實(shí)例;
     private Object readResolve() {     
              return INSTANCE;     
     }    
 }   

2.classloader。

stackverflow等地方給出的答案,但是我沒能理解怎么用。。。。。:

private static Class getClass(String clazz) throws ClassNotFoundException {
      ClassLoader loader = Thread.currentThread().getContextClassLoader();
      if(loader == null)
         loader = YourSingleton.class.getClassLoader();
      return (loader.loadClass(clazz));
   }
}

3.clone

單例類,不應(yīng)該實(shí)現(xiàn)clone方法,只要不實(shí)現(xiàn),就沒辦法clone的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容