cuiyi's blog(崔毅 crazycy)

          記錄點滴 鑒往事之得失 以資于發展
          數據加載中……

          單例模式(SingLeton Pattern)的誤區

          單例(SingLeton)故名思義就是在一個JVM運行中只有一個對象存在;請你務必注意到是在一個JVM虛擬機內。

          今天一個朋友問過這樣一個問題:為什么他的單例每次都進入構造函數,程序如下:

          public?class?ServerThread?implements?Runnable{

          ?????
          private?static?ServerThread?instance?=?null;??

          ?????
          private?ServerThread()?{

          ?????????
          try?{

          ?????????????
          if?(socket?==?null)?{

          ?????????????????socket?
          =?new?DatagramSocket(Constants.SERVER_PORT);

          ?????????????}

          ??????????}?
          catch?(Exception?e)?{

          ?????????????????e.printStackTrace();

          ???????????}

          ???????}

          ?????
          public?static?final?ServerThread?getInstance()?{

          ????????
          if?(instance?==?null)?{
          ???????????System.out.println(
          "instance?is?null?new?generate.");
          ???????????instance?
          =?new?ServerThread();
          ????????}
          ????????
          return?instance;

          ??????}

          ????
          public?Thread?getThreadInstance()?{

          ????????
          if?(threadInstance?==?null)?{

          ????????????threadInstance?
          =?new?Thread(instance);
          ????????}
          ????????
          return?threadInstance;
          ????}

          。。。。。。。。

          }

          public?class?Main?{

          ??????main函數有servTr?
          =?ServerThread.getInstance().getThreadInstance();一句

          代碼沒有問題吧;為什么他的單例每次都進入構造函數呢?

          細問之下才知道了他啟動了兩次Main類;啟動一個Main類便在一個JVM,兩次的單例怎么能是同一個呢?!

          記住:是在同一個JVM中唯一,不是在一個物理內存中唯一。當然后者不是不能做到的,分布式對象嘛,呵呵。


          第二點就是:單例的構造:

          目前方式多種,我認為只有兩種是正確的:

          第一:private final static DaoFactory instance = new DaoFactory();

          第二:

          private?static?DaoFactory?instance?=?null;

          ??????
          private?Properties?props?=?null;

          ???????
          private?Map?daos?=?null;

          ??????
          private?DaoFactory()?{

          ???????????props?
          =?new?Properties();

          ???????????daos?
          =?new?HashMap();

          ??????}

          ?????

          ??????
          public?static?DaoFactory?getInstance()?{

          ???????????
          while?(null?==?instance)?{

          ?????????????????
          synchronized(DaoFactory.class)?{

          ??????????????????????
          if?(null?==?instance)?{

          ????????????????????????????instance?
          =?new?DaoFactory();

          ????????????????????????????instance.initDaoFactroy();

          ??????????????????????}

          ?????????????????}

          ???????????}

          ???????????
          return?instance;

          ??????}


          看清楚了,這里用的是while;不是if。

          為什么不能用if呢?下面的一個例子說明

          public?class?DomainFactory?{

          ??????
          private?List?provinces;
          ?
          ???????????
          private?Map?domains;

          ??????
          private?static?DomainFactory?instance;

          ??????
          private?DomainFactory()?{

          ???????????generateDomainTree();

          ??????}

          ??????
          public?static?DomainFactory?getInstance()?{

          ???????????
          if?(null?==?instance)?{

          ?????????????????
          synchronized?(DomainFactory.class)?{

          ??????????????????????
          if?(null?==?instance)?{

          ????????????????????????????instance?
          =?new?DomainFactory();

          ??????????????????????}

          ?????????????????}

          ???????????}

          ???????????
          return?instance;

          ??????}

          ??????
          private?void?generateDomainTree()?{

          。。。。。

          }


          ?

          public?class?CategoryFactory?{

          ??????
          private?Map?map;?????

          ??????
          private?static?CategoryFactory?instance;
          ?????

          ??????
          private?CategoryFactory()?{

          ???????????map?
          =?new?HashMap();

          ???????????generateCategoryTree();

          ??????}?????

          ??????
          public?static?CategoryFactory?getInstance()?{

          ???????????
          if?(null?==?instance)?{

          ?????????????????
          synchronized?(CategoryFactory.class)?{

          ??????????????????????
          if?(null?==?instance)?{

          ????????????????????????????instance?
          =?new?CategoryFactory();

          ??????????????????????}

          ?????????????????}

          ???????????}

          ???????????
          return?instance;

          ??????}?????

          ??????
          private?void?generateCategoryTree()?{

          。。。。。。

          }

          ?

          public?class?SearchAction?extends?DispatchAction?{

          ??????
          public?ActionForward?getCatalogData(ActionMapping?mapping,?ActionForm?form,

          ?????????????????HttpServletRequest?request,?HttpServletResponse?response)?{

          ???????????DomainObject?domainObject?
          =?new?DomainObject();

          ???????????domainObject.setCity(
          "b");

          ???????????request.getSession().setAttribute(
          "domainObject",?domainObject);

          ???????????request.setAttribute(
          "domains",?DomainFactory.getInstance().getCity(domainObject.getCity()));

          ???????????
          return?mapping.findForward("left");

          ??????}????
          ?

          ??????
          public?ActionForward?getCatalogDataCategory(ActionMapping?mapping,?ActionForm?form,

          ?????????????????HttpServletRequest?request,?HttpServletResponse?response)?{

          ???????????request.setAttribute(
          "productCategories",?CategoryFactory.getInstance().getRootProductCategories());

          ???????????
          return?mapping.findForward("body");

          ??????}

          <frameset?rows="80,668*"?cols="*"?frameborder="no"?border="0"?framespacing="0">

          ????
          <frame?name="header"?src="./search_header.jsp"?scrolling="no"?title="header"?noresize="noresize">

          ????
          <frameset?cols="180,*"?frameborder="yes"?border="0"?framespacing="0">

          ???????
          <frame?name="left"?scrolling="no"?src="search.html?method=getCatalogData"?target="_self"?noresize="noresize">

          ???????
          <frame?name="body"?scrolling="yes"?src="search.html?method=getCatalogDataCategory"?scrolling="yes"?target="_self"?noresize="noresize">
          ??
          </frameset>
          </frameset>

          用if 出錯了吧?為什么,同時進入了if(null == instance)的判斷,然后爭奪鎖;結果爭奪不到的只能在爭奪到后容易出現問題,具體這里說不清楚,希望明白人跟貼進一步討論之

          posted on 2006-07-15 00:02 crazycy 閱讀(2539) 評論(13)  編輯  收藏 所屬分類: Design Pattern、JEE Pattern

          評論

          # re: 單例模式(SingLeton Pattern)的誤區  回復  更多評論   

          改為
          public synchronized static DaoFactory getInstance() {
          if(instance == null) {
          instance = new DaoFactory();
          instance.initDaoFactroy();
          }
          }
          }
          2006-07-15 08:00 | badqiu

          # re: 單例模式(SingLeton Pattern)的誤區  回復  更多評論   

          我想問問,在一個web server,比如tomcat下,放兩個application。
          在這兩個application中都包含有同一個單實例的class。那么這兩個單實例會互相有影響么?為什么在web系統中很少見人使用單實例,實際的例子就是spring取對象的方式,直接做一個單實例封裝一下就可以在系統內方便的取對象了,為什么還要用WebApplicationContextUtils.getRequiredWebApplicationContext(context)的方式來取,我看代碼spring 是把啟動時加載的配置信息放在ServletContext里,為什么不直接放在一個單實例里取值還方便,對單實例一直有疑惑,還請老兄賜教
          2006-07-15 08:16 | mixlee

          # re: 單例模式(SingLeton Pattern)的誤區  回復  更多評論   

          @mixlee
          兩個application的classpath不一樣,放在WEB-INF里面的lib是不能共享的,spring的ApplicationContext是跟具體的webapp相關的,當然不能做成全局的單例,只能做成一個ServletContext里面的單例
          2006-07-15 12:03 | quaff

          # re: 單例模式(SingLeton Pattern)的誤區  回復  更多評論   

          關于單例
          懶漢和餓漢式的是最安全的.這里有篇文章不妨參考一下
          http://www-128.ibm.com/developerworks/java/library/j-dcl.html?dwzone=java
          2006-07-15 12:39 | binge

          # re: 單例模式(SingLeton Pattern)的誤區  回復  更多評論   

          @badqiu
          呵呵

          多謝多謝

          今天我用while也遇到了和if一樣的問題
          看樣子,的確是單例實現方式的問題
          現在我用你推薦的方案改進一下
          2006-07-15 16:19 | crazycy

          # re: 單例模式(SingLeton Pattern)的誤區  回復  更多評論   

          @binge
          我用的就是你推薦的文章的Double-checked locking哦

          奇怪了 我這個地方是同時搶占;而且是肯定發生的;

          哎...以前一直這么用,只是取instance的線程同時搶占的幾率小,呵呵,問題的深入解決看來還需要多過河才行(多過河找到不濕腳的辦法,呵呵)啊
          2006-07-15 16:23 | crazycy

          # re: 單例模式(SingLeton Pattern)的誤區  回復  更多評論   

          @quaff
          既然兩個沒影響,那做成單例有什么不可,反正各自加載各自的配置文件,互不影響。
          2006-07-15 21:25 | mixlee

          # re: 單例模式(SingLeton Pattern)的誤區  回復  更多評論   

          @crazycy
          nono,文章寫到:
          The issue of the failure of double-checked locking is not due to implementation bugs in JVMs but to the current Java platform memory model. The memory model allows what is known as "out-of-order writes" and is a prime reason why this idiom fails.

          文章中說,基于java的內存模型,DCL并不是最安全的.最安全的是餓漢 和懶漢.
          當然,懶漢很影響效率.
          2006-07-15 22:28 | binge

          # re: 單例模式(SingLeton Pattern)的誤區  回復  更多評論   

          記?。菏窃谕粋€JVM中唯一,不是在一個物理內存中唯一。當然后者不是不能做到的,分布式對象嘛,呵呵。


          看了很受啟發,想問一下,如果我希望在一個物理內存中唯一,甚至在多個應用服務器的cluster環境中唯一,有什么方法嗎?
          2006-08-24 14:25 | merrymei

          # re: 單例模式(SingLeton Pattern)的誤區  回復  更多評論   

          @merrymei

          好像也提供不出解決方案.......
          2006-09-01 10:51 | crazycy

          # re: 單例模式(SingLeton Pattern)的誤區  回復  更多評論   

          DCL方式在java里是行不通的。 希望不要誤導別人
          2006-09-20 14:42 | freshman

          # re: 單例模式(SingLeton Pattern)的誤區  回復  更多評論   

          @freshman
          接受建議
          2006-09-20 22:23 | crazycy

          # re: 單例模式(SingLeton Pattern)的誤區  回復  更多評論   

          使用 synchronized 關鍵字就可以解決鎖爭奪的問題
          2006-10-11 09:44 | www.坑你.net
          主站蜘蛛池模板: 克拉玛依市| 淅川县| 青岛市| 通州市| 浑源县| 天柱县| 冀州市| 邢台市| 土默特右旗| 龙井市| 莆田市| 金乡县| 托克托县| 宣城市| 土默特左旗| 四子王旗| 靖边县| 玉龙| 桃江县| 启东市| 金昌市| 瓮安县| 舞阳县| 宜兴市| 勐海县| 赣州市| 定边县| 化德县| 昆山市| 通许县| 邻水| 文化| 郓城县| 大竹县| 南川市| 黄大仙区| 文安县| 桓仁| 昌图县| 大石桥市| 民丰县|