模式回顧---單例

          最近突然想回顧一下設(shè)計(jì)模式,很多東西是要回過(guò)頭來(lái)總結(jié)一下的。今天先回顧一下單例吧。
          很多時(shí)候覺得挺搞笑的,去面試的時(shí)候如果人家問(wèn)你設(shè)計(jì)模式,一般都是要你寫個(gè)單例模式。去年來(lái)北京好幾家面試都是問(wèn)我這個(gè)。當(dāng)時(shí)我就想這個(gè)能反映出一個(gè)人的水平來(lái)嗎?還是說(shuō)更多的是反映出這個(gè)公司的水平呢?
          隨著一年的應(yīng)用,很多地方都用過(guò)之后覺得,單例這個(gè)東西雖然簡(jiǎn)單,可是現(xiàn)實(shí)是復(fù)雜的。所以單例這個(gè)簡(jiǎn)單的模式也不能太小瞧咯。
          單例其實(shí)有很多種實(shí)現(xiàn),這是其中的一種,延遲加載的(好像英文叫Lazy?):
          [下面代碼中所有的構(gòu)造器都是私有的,這里我就省略不寫了。]
          public class ClassName {
              
          public static ClassName getInstance(){
                  
          if(instance == null)
                  {
                      instance 
          = new ClassName();
                  }
                  
          return instance;
              }
              
              
          private static ClassName instance;
          }
          這種的好處是我們的單例使用時(shí)才進(jìn)行初始化,這樣方便我們?cè)谙到y(tǒng)啟動(dòng)時(shí)做些小動(dòng)作。但是這個(gè)方式不是線程安全的,想要完成一個(gè)線程安全的單例,有幾種方式:
          (一)
          public class ClassName {
              
          public static ClassName getInstance(){
                  
          return instance;
              }
              
              
          private static ClassName instance = new ClassName();
          }
          這種方式,可以保證我們的單例是線程安全的,畢竟我們唯一的實(shí)例在系統(tǒng)初始化的時(shí)候就構(gòu)造了。可是Java的機(jī)制是static級(jí)別的變量初始化時(shí)互相調(diào)用是會(huì)報(bào)異常的。所以隨著系統(tǒng)的擴(kuò)展,尤其還會(huì)有一些新手或者粗心大意的家伙(比如說(shuō),我)會(huì)亂用你的方法。一不小心就造成問(wèn)題了。而且,你也失去了第一個(gè)方式中的一個(gè)小優(yōu)勢(shì),不能在系統(tǒng)啟動(dòng)時(shí)做點(diǎn)小動(dòng)作了。
          (二)
          public class ClassName {
              
          public static synchronized ClassName getInstance(){
                  
          if(instance == null)
                  {
                       instance 
          = new ClassName();
                  }
                  
          return instance;
              }
              
              
          private static ClassName instance;
          }
          這樣倒是線程安全了,也可以延遲加載,但是從今以后這個(gè)getInstance方法就是synchronized的了,那絕對(duì)是很影響效率的。我跟朋友討論提出了幾種寫法,以期既能使單例可以在系統(tǒng)啟動(dòng)不至于數(shù)據(jù)已經(jīng)煮成熟飯又是線程安全的:(少數(shù)人討論結(jié)果,代碼可能會(huì)比較丑陋,僅供參考,歡迎拍磚)

          public class ClassName {
              
          public static ClassName getInstance(){
                  
          if(instance == null)
                  {
                       instance 
          = ClassName.createInstance();
                  }
                  
          return instance;
              }
              
              
          private static synchronized ClassName createInstance(){
                  
          if(instance == null)
                  {    
                      
          return new ClassName();
                  }
          else{
                      
          return instance;
                  }
              }
              
              
          private static ClassName instance;
          }
          這種寫法就很好的解決了這些問(wèn)題。
          還有一種寫法是這樣的,這個(gè)不是延遲加載的。而是采用了一種取巧的方式。
          public class ClassName {
              
          public static ClassName getInstance(){
                  
          if(!instance.isInit)
                  {
                       instance.initSingleton();
                  }
                  
          return instance;
              }
              
              
          private synchronized void initSingleton() {
                
          if(!isInit)
                {
                    reset();
          //這名字是有點(diǎn)怪異,我沒時(shí)間想太好聽的名字
                    isInit = true;
                }
              }
              
              
          public void  reset(){
                  
          //.....真正進(jìn)行數(shù)據(jù)初始化的地方
              }
              
              
          private boolean isInit = false;
              
              
          private static ClassName instance = new ClassName();
          }

          將所有的初始化代碼搬到構(gòu)造器之外。這是專為數(shù)據(jù)初始化和復(fù)位進(jìn)行的設(shè)計(jì)。所以我把reset開放了出來(lái)。



          posted on 2008-01-29 21:59 咖啡屋的鼠標(biāo) 閱讀(1459) 評(píng)論(7)  編輯  收藏 所屬分類: Java

          評(píng)論

          # re: 模式回顧---單例 2008-01-30 09:30 久城

          我一般用的都是這種:
          public class ClassName {
          public static ClassName getInstance(){
          if(instance == null)
          {
          instance = new ClassName();
          }
          return instance;
          }

          private static ClassName instance;
          }  回復(fù)  更多評(píng)論   

          # re: 模式回顧---單例 2008-01-30 10:05 大衛(wèi)

          不錯(cuò)!  回復(fù)  更多評(píng)論   

          # re: 模式回顧---單例 2008-01-30 10:51 dennis

          public class ClassName {
          public static ClassName getInstance(){
          if(instance == null)
          {
          instance = ClassName.createInstance();
          }
          return instance;
          }

          private static synchronized ClassName createInstance(){
          return new ClassName();
          }

          private static ClassName instance;
          }
          這種寫法有問(wèn)題的,可以想象下,線程A判斷instance==null,然后開始createInstance,正在此時(shí),線程B也判斷instance==null,然后等待線程A完成創(chuàng)建并解鎖,然后線程B也createInstance,這還是單例嗎?單例模式的寫法網(wǎng)上已經(jīng)討論爛了,這樣的錯(cuò)誤實(shí)在不應(yīng)該。  回復(fù)  更多評(píng)論   

          # re: 模式回顧---單例 2008-01-30 10:52 dennis

          第三種寫法就更無(wú)語(yǔ)了,靜態(tài)初始化,更談不上所謂的lazy initialize了。基礎(chǔ)還是好好看下吧。  回復(fù)  更多評(píng)論   

          # re: 模式回顧---單例 2008-01-30 10:54 dennis

          ps:我所說(shuō)的第三種是最后那個(gè)。
          既然發(fā)表在blogjava首頁(yè)上,文章至少要保證沒有錯(cuò)誤,不然誤導(dǎo)人也是害人害己,說(shuō)的嚴(yán)重了,博主自己看看。  回復(fù)  更多評(píng)論   

          # re: 模式回顧---單例 2008-01-30 11:45 咖啡屋的鼠標(biāo)

          @dennis
          感謝您的批評(píng)指正,這個(gè)寫法是有點(diǎn)問(wèn)題。昨天跟朋友討論出這個(gè)方法的時(shí)候沒考慮細(xì),剛才看了看,改改也能使用,可能就丑陋點(diǎn)了。先刪去。

          至于最后一種,確實(shí)不是lazy的,我看了看也沒寫是Lazy的,應(yīng)該不會(huì)有誤導(dǎo)概念的問(wèn)題,但還是標(biāo)明了一下以防止誤會(huì),很感謝你的指出。我只是把所有的初始化代碼放到了init里面,在構(gòu)造器里不做任何操作初始化操作,其實(shí)還應(yīng)該做一次是否初始化的二次判斷。省的多次初始化,但是那樣我就還要再寫一個(gè)函數(shù)來(lái)控制復(fù)位,說(shuō)起來(lái)可能會(huì)更繁瑣,就沒加。

            回復(fù)  更多評(píng)論   

          # re: 模式回顧---單例 2008-01-30 13:18 咖啡屋的鼠標(biāo)

          考慮了一下,還是趁中午的時(shí)間把改正的結(jié)果更新了,經(jīng)過(guò)簡(jiǎn)單的多線程測(cè)試是沒有問(wèn)題的。  回復(fù)  更多評(píng)論   


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          <2008年1月>
          303112345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          導(dǎo)航

          統(tǒng)計(jì)

          常用鏈接

          留言簿(15)

          隨筆分類(52)

          隨筆檔案(76)

          文章分類(3)

          文章檔案(4)

          新聞檔案(1)

          收藏夾

          Flex

          搜索

          積分與排名

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 横山县| 连山| 兴安盟| 若羌县| 杭锦后旗| 惠水县| 临海市| 思茅市| 元氏县| 布拖县| 辛集市| 沁源县| 枣阳市| 阆中市| 浏阳市| 乡城县| 柳州市| 丰都县| 安远县| 镇远县| 武穴市| 顺昌县| 大悟县| 五河县| 滦南县| 荥经县| 淳安县| 碌曲县| 马龙县| 凉城县| 电白县| 沁水县| 北碚区| 万全县| 正阳县| 宁强县| 页游| 蓬安县| 贡山| 西青区| 读书|