單例模式

轉(zhuǎn)載單例模式的七種寫(xiě)法
代碼地址GitHub

java單例的七種寫(xiě)法##

第一種(懶漢,線(xiàn)程不安全):

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}   
    public static Singleton getInstance() {  
        if (instance == null) {  
          instance = new Singleton();  
        }  
        return instance;  
    }  
}

這種寫(xiě)法lazy loading很明顯,但是致命的是在多線(xiàn)程不能正常工作。

第二種(懶漢,線(xiàn)程安全):

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}
    public static synchronized Singleton getInstance() {  
        if (instance == null) {  
            instance = new Singleton();  
        }  
        return instance;  
    }  
}

這種寫(xiě)法能夠在多線(xiàn)程中很好的工作,而且看起來(lái)它也具備很好的lazy loading,但是,遺憾的是,效率很低,大多數(shù)情況下不需要同步。

第三種(餓漢):

public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}
    public static Singleton getInstance() {  
    return instance;  
    }  
}  

這種方式基于classloder機(jī)制避免了多線(xiàn)程的同步問(wèn)題,不過(guò),instance在類(lèi)裝載時(shí)就實(shí)例化,雖然導(dǎo)致類(lèi)裝載的原因有很多種,在單例模式中大多數(shù)都是調(diào)用getInstance方法, 但是也不能確定有其他的方式(或者其他的靜態(tài)方法)導(dǎo)致類(lèi)裝載,這時(shí)候初始化instance顯然沒(méi)有達(dá)到lazy loading的效果。

第四種(餓漢,變種):

 public class Singleton {  
     private Singleton instance = null;  
     static {  
        instance = new Singleton();  
     }  
     private Singleton (){}
     public static Singleton getInstance() {  
        return this.instance;  
     }  
}  

表面上看起來(lái)差別挺大,其實(shí)更第三種方式差不多,都是在類(lèi)初始化即實(shí)例化instance。

第五種(靜態(tài)內(nèi)部類(lèi)):

public class Singleton {  
     private static class SingletonHolder {  
     private static final Singleton INSTANCE = new Singleton();  
     }  
     private Singleton (){}
     public static final Singleton getInstance() {  
         return SingletonHolder.INSTANCE;  
     }  
}  

這種方式同樣利用了classloder的機(jī)制來(lái)保證初始化instance時(shí)只有一個(gè)線(xiàn)程,它跟第三種和第四種方式不同的是(很細(xì)微的差別):第三種和第四種方式是只要Singleton類(lèi)被裝載了,那么instance就會(huì)被實(shí)例化(沒(méi)有達(dá)到lazy loading效果),而這種方式是Singleton類(lèi)被裝載了,instance不一定被初始化。因?yàn)镾ingletonHolder類(lèi)沒(méi)有被主動(dòng)使用,只有顯示通過(guò)調(diào)用getInstance方法時(shí),才會(huì)顯示裝載SingletonHolder類(lèi),從而實(shí)例化instance。想象一下,如果實(shí)例化instance很消耗資源,我想讓他延遲加載,另外一方面,我不希望在Singleton類(lèi)加載時(shí)就實(shí)例化,因?yàn)槲也荒艽_保Singleton類(lèi)還可能在其他的地方被主動(dòng)使用從而被加載,那么這個(gè)時(shí)候?qū)嵗痠nstance顯然是不合適的。這個(gè)時(shí)候,這種方式相比第三和第四種方式就顯得很合理。

第六種(枚舉):

public enum Singleton {  
     INSTANCE;  
     public void whateverMethod() {  
     }  
 }  

這種方式是Effective Java作者Josh Bloch 提倡的方式,它不僅能避免多線(xiàn)程同步問(wèn)題,而且還能防止反序列化重新創(chuàng)建新的對(duì)象,可謂是很堅(jiān)強(qiáng)的壁壘啊,不過(guò),個(gè)人認(rèn)為由于1.5中才加入enum特性,用這種方式寫(xiě)不免讓人感覺(jué)生疏,在實(shí)際工作中,我也很少看見(jiàn)有人這么寫(xiě)過(guò)。

第七種(雙重校驗(yàn)鎖):

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

這個(gè)是第二種方式的升級(jí)版,俗稱(chēng)雙重檢查鎖定,詳細(xì)介紹請(qǐng)查看:http://www.ibm.com/developerworks/cn/java/j-dcl.html

在JDK1.5之后,雙重檢查鎖定才能夠正常達(dá)到單例效果。

總結(jié)
有兩個(gè)問(wèn)題需要注意:
1、如果單例由不同的類(lèi)裝載器裝入,那便有可能存在多個(gè)單例類(lèi)的實(shí)例。假定不是遠(yuǎn)端存取,例如一些servlet容器對(duì)每個(gè)servlet使用完全不同的類(lèi) 裝載器,這樣的話(huà)如果有兩個(gè)servlet訪(fǎng)問(wèn)一個(gè)單例類(lèi),它們就都會(huì)有各自的實(shí)例。
2、如果Singleton實(shí)現(xiàn)了java.io.Serializable接口,那么這個(gè)類(lèi)的實(shí)例就可能被序列化和復(fù)原。不管怎樣,如果你序列化一個(gè)單例類(lèi)的對(duì)象,接下來(lái)復(fù)原多個(gè)那個(gè)對(duì)象,那你就會(huì)有多個(gè)單例類(lèi)的實(shí)例。

對(duì)第一個(gè)問(wèn)題修復(fù)的辦法是:
private static Class getClass(String classname)
throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

      if(classLoader == null)     
         classLoader = Singleton.class.getClassLoader();     
      
      return (classLoader.loadClass(classname));     
   }     
}  

對(duì)第二個(gè)問(wèn)題修復(fù)的辦法是:

public class Singleton implements java.io.Serializable {     
   public static Singleton INSTANCE = new Singleton();     
      
   protected Singleton() {     
        
   }     
   private Object readResolve() {     
            return INSTANCE;     
      }    
}   

第三種和第五種方式,簡(jiǎn)單易懂,而且在JVM層實(shí)現(xiàn)了線(xiàn)程安全(如果不是多個(gè)類(lèi)加載器環(huán)境),一般的情況下,使用第三種方式,只有在要明確實(shí)現(xiàn)lazy loading效果時(shí)才會(huì)使用第五種方式,另外,如果涉及到反序列化創(chuàng)建對(duì)象時(shí)會(huì)試著使用枚舉的方式來(lái)實(shí)現(xiàn)單例,不過(guò),一直會(huì)保證的程序是線(xiàn)程安全的,而且永遠(yuǎn)不會(huì)使用第一種和第二種方式,如果有其他特殊的需求,會(huì)使用第七種方式,畢竟,JDK1.5已經(jīng)沒(méi)有雙重檢查鎖定的問(wèn)題了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容