一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

單例模式

 星君達(dá) 2015-10-27

本文繼續(xù)介紹23種設(shè)計(jì)模式系列之單例模式。

概念:
  java中單例模式是一種常見的設(shè)計(jì)模式,單例模式的寫法有好幾種,這里主要介紹三種:懶漢式單例、餓漢式單例、登記式單例。
  單例模式有以下特點(diǎn):
  1、單例類只能有一個(gè)實(shí)例。
  2、單例類必須自己創(chuàng)建自己的唯一實(shí)例。
  3、單例類必須給所有其他對(duì)象提供這一實(shí)例。
  單例模式確保某個(gè)類只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例。在計(jì)算機(jī)系統(tǒng)中,線程池、緩存、日志對(duì)象、對(duì)話框、打印機(jī)、顯卡的驅(qū)動(dòng)程序?qū)ο蟪1辉O(shè)計(jì)成單例。這些應(yīng)用都或多或少具有資源管理器的功能。每臺(tái)計(jì)算機(jī)可以有若干個(gè)打印機(jī),但只能有一個(gè)Printer Spooler,以避免兩個(gè)打印作業(yè)同時(shí)輸出到打印機(jī)中。每臺(tái)計(jì)算機(jī)可以有若干通信端口,系統(tǒng)應(yīng)當(dāng)集中管理這些通信端口,以避免一個(gè)通信端口同時(shí)被兩個(gè)請(qǐng)求同時(shí)調(diào)用??傊x擇單例模式就是為了避免不一致狀態(tài),避免政出多頭。


一、懶漢式單例

  1. //懶漢式單例類.在第一次調(diào)用的時(shí)候?qū)嵗约?nbsp;  
  2. public class Singleton {  
  3.     private Singleton() {}  
  4.     private static Singleton single=null;  
  5.     //靜態(tài)工廠方法   
  6.     public static Singleton getInstance() {  
  7.          if (single == null) {    
  8.              single = new Singleton();  
  9.          }    
  10.         return single;  
  11.     }  
  12. }  

Singleton通過將構(gòu)造方法限定為private避免了類在外部被實(shí)例化,在同一個(gè)虛擬機(jī)范圍內(nèi),Singleton的唯一實(shí)例只能通過getInstance()方法訪問。

(事實(shí)上,通過Java反射機(jī)制是能夠?qū)嵗瘶?gòu)造方法為private的類的,那基本上會(huì)使所有的Java單例實(shí)現(xiàn)失效。此問題在此處不做討論,姑且掩耳盜鈴地認(rèn)為反射機(jī)制不存在。)

但是以上懶漢式單例的實(shí)現(xiàn)沒有考慮線程安全問題,它是線程不安全的,并發(fā)環(huán)境下很可能出現(xiàn)多個(gè)Singleton實(shí)例,要實(shí)現(xiàn)線程安全,有以下三種方式,都是對(duì)getInstance這個(gè)方法改造,保證了懶漢式單例的線程安全,如果你第一次接觸單例模式,對(duì)線程安全不是很了解,可以先跳過下面這三小條,去看餓漢式單例,等看完后面再回頭考慮線程安全的問題:


1、在getInstance方法上加同步

  1. public static synchronized Singleton getInstance() {  
  2.          if (single == null) {    
  3.              single = new Singleton();  
  4.          }    
  5.         return single;  
  6. }  

2、雙重檢查鎖定

  1. public static Singleton getInstance() {  
  2.         if (singleton == null) {    
  3.             synchronized (Singleton.class) {    
  4.                if (singleton == null) {    
  5.                   singleton = new Singleton();   
  6.                }    
  7.             }    
  8.         }    
  9.         return singleton;   
  10.     }  

3、靜態(tài)內(nèi)部類

  1. public class Singleton {    
  2.     private static class LazyHolder {    
  3.        private static final Singleton INSTANCE = new Singleton();    
  4.     }    
  5.     private Singleton (){}    
  6.     public static final Singleton getInstance() {    
  7.        return LazyHolder.INSTANCE;    
  8.     }    
  9. }    
這種比上面1、2都好一些,既實(shí)現(xiàn)了線程安全,又避免了同步帶來(lái)的性能影響。


二、餓漢式單例

  1. //餓漢式單例類.在類初始化時(shí),已經(jīng)自行實(shí)例化   
  2. public class Singleton1 {  
  3.     private Singleton1() {}  
  4.     private static final Singleton1 single = new Singleton1();  
  5.     //靜態(tài)工廠方法   
  6.     public static Singleton1 getInstance() {  
  7.         return single;  
  8.     }  
  9. }  
餓漢式在類創(chuàng)建的同時(shí)就已經(jīng)創(chuàng)建好一個(gè)靜態(tài)的對(duì)象供系統(tǒng)使用,以后不再改變,所以天生是線程安全的。


三、登記式單例(可忽略)

  1. //類似Spring里面的方法,將類名注冊(cè),下次從里面直接獲取。  
  2. public class Singleton3 {  
  3.     private static Map<String,Singleton3> map = new HashMap<String,Singleton3>();  
  4.     static{  
  5.         Singleton3 single = new Singleton3();  
  6.         map.put(single.getClass().getName(), single);  
  7.     }  
  8.     //保護(hù)的默認(rèn)構(gòu)造子  
  9.     protected Singleton3(){}  
  10.     //靜態(tài)工廠方法,返還此類惟一的實(shí)例  
  11.     public static Singleton3 getInstance(String name) {  
  12.         if(name == null) {  
  13.             name = Singleton3.class.getName();  
  14.             System.out.println("name == null"+"--->name="+name);  
  15.         }  
  16.         if(map.get(name) == null) {  
  17.             try {  
  18.                 map.put(name, (Singleton3) Class.forName(name).newInstance());  
  19.             } catch (InstantiationException e) {  
  20.                 e.printStackTrace();  
  21.             } catch (IllegalAccessException e) {  
  22.                 e.printStackTrace();  
  23.             } catch (ClassNotFoundException e) {  
  24.                 e.printStackTrace();  
  25.             }  
  26.         }  
  27.         return map.get(name);  
  28.     }  
  29.     //一個(gè)示意性的商業(yè)方法  
  30.     public String about() {      
  31.         return "Hello, I am RegSingleton.";      
  32.     }      
  33.     public static void main(String[] args) {  
  34.         Singleton3 single3 = Singleton3.getInstance(null);  
  35.         System.out.println(single3.about());  
  36.     }  
  37. }  

 登記式單例實(shí)際上維護(hù)了一組單例類的實(shí)例,將這些實(shí)例存放在一個(gè)Map(登記?。┲校瑢?duì)于已經(jīng)登記過的實(shí)例,則從Map直接返回,對(duì)于沒有登記的,則先登記,然后返回。 

這里我對(duì)登記式單例標(biāo)記了可忽略,我的理解來(lái)說,首先它用的比較少,另外其實(shí)內(nèi)部實(shí)現(xiàn)還是用的餓漢式單例,因?yàn)槠渲械膕tatic方法塊,它的單例在類被裝載的時(shí)候就被實(shí)例化了。


餓漢式和懶漢式區(qū)別

從名字上來(lái)說,餓漢和懶漢,

餓漢就是類一旦加載,就把單例初始化完成,保證getInstance的時(shí)候,單例是已經(jīng)存在的了,

而懶漢比較懶,只有當(dāng)調(diào)用getInstance的時(shí)候,才回去初始化這個(gè)單例。

另外從以下兩點(diǎn)再區(qū)分以下這兩種方式:


1、線程安全:

餓漢式天生就是線程安全的,可以直接用于多線程而不會(huì)出現(xiàn)問題,

懶漢式本身是非線程安全的,為了實(shí)現(xiàn)線程安全有幾種寫法,分別是上面的1、2、3,這三種實(shí)現(xiàn)在資源加載和性能方面有些區(qū)別。



2、資源加載和性能:

餓漢式在類創(chuàng)建的同時(shí)就實(shí)例化一個(gè)靜態(tài)對(duì)象出來(lái),不管之后會(huì)不會(huì)使用這個(gè)單例,都會(huì)占據(jù)一定的內(nèi)存,但是相應(yīng)的,在第一次調(diào)用時(shí)速度也會(huì)更快,因?yàn)槠滟Y源已經(jīng)初始化完成,

而懶漢式顧名思義,會(huì)延遲加載,在第一次使用該單例的時(shí)候才會(huì)實(shí)例化對(duì)象出來(lái),第一次調(diào)用時(shí)要做初始化,如果要做的工作比較多,性能上會(huì)有些延遲,之后就和餓漢式一樣了。

至于1、2、3這三種實(shí)現(xiàn)又有些區(qū)別,

第1種,在方法調(diào)用上加了同步,雖然線程安全了,但是每次都要同步,會(huì)影響性能,畢竟99%的情況下是不需要同步的,

第2種,在getInstance中做了兩次null檢查,確保了只有第一次調(diào)用單例的時(shí)候才會(huì)做同步,這樣也是線程安全的,同時(shí)避免了每次都同步的性能損耗

第3種,利用了classloader的機(jī)制來(lái)保證初始化instance時(shí)只有一個(gè)線程,所以也是線程安全的,同時(shí)沒有性能損耗,所以一般我傾向于使用這一種。


什么是線程安全?

如果你的代碼所在的進(jìn)程中有多個(gè)線程在同時(shí)運(yùn)行,而這些線程可能會(huì)同時(shí)運(yùn)行這段代碼。如果每次運(yùn)行結(jié)果和單線程運(yùn)行的結(jié)果是一樣的,而且其他的變量的值也和預(yù)期的是一樣的,就是線程安全的。

或者說:一個(gè)類或者程序所提供的接口對(duì)于線程來(lái)說是原子操作,或者多個(gè)線程之間的切換不會(huì)導(dǎo)致該接口的執(zhí)行結(jié)果存在二義性,也就是說我們不用考慮同步的問題,那就是線程安全的。


應(yīng)用

以下是一個(gè)單例類使用的例子,以懶漢式為例,這里為了保證線程安全,使用了雙重檢查鎖定的方式

  1. public class TestSingleton {  
  2.     String name = null;  
  3.   
  4.         private TestSingleton() {  
  5.     }  
  6.   
  7.     private static volatile TestSingleton instance = null;  
  8.   
  9.     public static TestSingleton getInstance() {  
  10.            if (instance == null) {    
  11.              synchronized (TestSingleton.class) {    
  12.                 if (singleton == null) {    
  13.                    singleton = new TestSingleton();   
  14.                 }    
  15.              }    
  16.            }   
  17.            return instance;  
  18.     }  
  19.   
  20.     public String getName() {  
  21.         return name;  
  22.     }  
  23.   
  24.     public void setName(String name) {  
  25.         this.name = name;  
  26.     }  
  27.   
  28.     public void printInfo() {  
  29.         System.out.println("the name is " + name);  
  30.     }  
  31.   
  32. }  

可以看到里面加了volatile關(guān)鍵字來(lái)聲明單例對(duì)象,既然synchronized已經(jīng)起到了多線程下原子性、有序性、可見性的作用,為什么還要加volatile呢,原因已經(jīng)在下面評(píng)論中提到,

還有疑問可參考http://www./topic/652440
和http://www.cs./~pugh/java/memoryModel/DoubleCheckedLocking.html


  1. public class TMain {  
  2.     public static void main(String[] args){  
  3.         TestStream ts1 = TestSingleton.getInstance();  
  4.         ts1.setName("jason");  
  5.         TestStream ts2 = TestSingleton.getInstance();  
  6.         ts2.setName("0539");  
  7.           
  8.         ts1.printInfo();  
  9.         ts2.printInfo();  
  10.           
  11.         if(ts1 == ts2){  
  12.             System.out.println("創(chuàng)建的是同一個(gè)實(shí)例");  
  13.         }else{  
  14.             System.out.println("創(chuàng)建的不是同一個(gè)實(shí)例");  
  15.         }  
  16.     }  
  17. }  

 運(yùn)行結(jié)果:


結(jié)論:由結(jié)果可以得知單例模式為一個(gè)面向?qū)ο蟮膽?yīng)用程序提供了對(duì)象惟一的訪問點(diǎn),不管它實(shí)現(xiàn)何種功能,整個(gè)應(yīng)用程序都會(huì)同享一個(gè)實(shí)例對(duì)象。

對(duì)于單例模式的幾種實(shí)現(xiàn)方式,知道餓漢式和懶漢式的區(qū)別,線程安全,資源加載的時(shí)機(jī),還有懶漢式為了實(shí)現(xiàn)線程安全的3種方式的細(xì)微差別。


    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    精品国产日韩一区三区| 日韩欧美一区二区不卡看片| 精品欧美日韩一区二区三区 | 日本午夜免费福利视频| 一区中文字幕人妻少妇| 久七久精品视频黄色的| 我想看亚洲一级黄色录像| 亚洲天堂一区在线播放| 欧美老太太性生活大片| 国产成人精品国内自产拍| 日本高清一道一二三区四五区| 亚洲品质一区二区三区| 熟妇人妻av中文字幕老熟妇| 日本人妻熟女一区二区三区| 99久久成人精品国产免费| 国产精品久久久久久久久久久痴汉 | 国产一区欧美一区日本道| 都市激情小说在线一区二区三区 | 国产一区二区三区草莓av| 国产一区二区三区四区免费| 中文字幕五月婷婷免费| 欧美又黑又粗大又硬又爽| 一区二区三区四区亚洲专区| 亚洲欧美中文字幕精品| 天堂热东京热男人天堂| 亚洲中文字幕在线观看黑人| 欧美一级内射一色桃子| 欧美午夜国产在线观看| 激情亚洲一区国产精品久久| 日本在线视频播放91| 国产亚洲神马午夜福利| 一级片黄色一区二区三区| 男女一进一出午夜视频| 狠狠亚洲丁香综合久久| 久热这里只有精品九九| 国产日韩欧美国产欧美日韩| 免费观看在线午夜视频| 亚洲综合激情另类专区老铁性| 国产日韩欧美专区一区| 伊人久久青草地综合婷婷| 国产大屁股喷水在线观看视频|