<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                28.4 享元模式的擴展 28.4.1 線程安全的問題 線程安全是一個老生常談的話題,只要使用Java開發都會遇到這個問題,我們之所以要在今天的享元模式中提到該問題,是因為該模式有太大的幾率發生線程不安全,為什么呢? 我們還以報考系統為例來說明這個問題。大家有沒有想過,為什么要以考試科目+考試地點作為外部狀態呢?為什么不能以考試科目或者考試地點作為外部狀態呢?這樣池中的對象會更少!可以!完全可以!我們把程序以考試科目為外部狀態,把享元工廠稍作修改,如代碼清單28-10所示。 代碼清單28-10 報考信息工廠 public?class?SignInfoFactory?{ ?????//池容器 ?????private?static?HashMap<String,SignInfo>?pool?=?new?HashMap<String,SignInfo>(); ?????//從池中獲得對象 ?????public?static?SignInfo?getSignInfo(String?key){ ?????????????//設置返回對象 ?????????????SignInfo?result?=?null; ?????????????//池中沒有該對象,則建立,并放入池中 ?????????????if(!pool.containsKey(key)){ ?????????????????????result?=?new?SignInfo(); ?????????????????????pool.put(key,?result); ?????????????}else{ ?????????????????????result?=?pool.get(key); ?????????????} ?????????????return?result; ?????} } 下面做很小的改動,只修改了黑色字體部分。為了展示多線程的情況,我們寫一個多線程的類,如代碼清單28-11所示。 代碼清單28-11 多線程場景 public?class?MultiThread?extends?Thread?{ ?????private?SignInfo?signInfo; ?????public?MultiThread(SignInfo?_signInfo){ ?????????????????????this.signInfo?=?_signInfo; ?????} ?????public?void?run(){ ?????????????if(!signInfo.getId().equals(signInfo.getLocation())){ ?????????????????????System.out.println("編號:"+signInfo.getId()); ?????????????????????System.out.println("考試地址:"+signInfo.getLocation()); ?????????????????????System.out.println("線程不安全了!"); ?????????????} ?????} } 在run方法中判斷特殊值,檢查是否是線程安全,我們來看看場景類,如代碼清單28-12所示。 代碼清單28-12 場景類 public?class?Client?{ ?????public?static?void?main(String[]?args)?{ ?????????????//在對象池中初始化4個對象 ?????????????SignInfoFactory.getSignInfo("科目1"); ?????????????SignInfoFactory.getSignInfo("科目2"); ?????????????SignInfoFactory.getSignInfo("科目3"); ?????????????SignInfoFactory.getSignInfo("科目4"); ?????????????//取得對象 ?????????????SignInfo?signInfo?=?SignInfoFactory.getSignInfo("科目2"); ?????????????while(true){ ?????????????????????signInfo.setId("ZhangSan"); ?????????????????????signInfo.setLocation("ZhangSan"); ?????????????????????(new?MultiThread(signInfo)).start(); ?????????????????????signInfo.setId("LiSi"); ?????????????????????signInfo.setLocation("LiSi"); ?????????????????????(new?MultiThread(signInfo)).start(); ?????????????} ?????} } 模擬實際的多線程情況,在對象池中我們保留4個對象,然后啟動N多個線程來模擬,我們馬上就看到如下的提示: 編號:LiSi 考試地址:ZhangSan 線程不安全了! 看看,線程不安全了吧,這是正常的,設置的享元對象數量太少,導致每個線程都到對象池中獲得對象,然后都去修改其屬性,于是就出現一些不和諧數據。只要使用Java開發,線程問題是不可避免的,那我們怎么去避免這個問題呢?享元模式是讓我們使用共享技術,而Java的多線程又有如此問題,該如何設計呢?沒什么可以參考的標準,只有依靠經驗,在需要的地方考慮一下線程安全,在大部分的場景下都不用考慮。我們在使用享元模式時,對象池中的享元對象盡量多,多到足夠滿足業務為止。 28.4.2 性能平衡 盡量使用Java基本類型作為外部狀態。在報考系統中,我們不考慮系統的修改風險,完全可以重新建立一個類作為外部狀態,因為這才完全符合面向對象編程的理念。好,我們實現處理,先看類圖,如圖28-4所示。 ![](https://box.kancloud.cn/2016-08-14_57b0036c3c7aa.jpg) 圖28-4 類作為外部狀態 我們首先來看ExtrinsicState外部狀態類,如代碼清單28-13所示。 代碼清單28-13 外部狀態類 public?class?ExtrinsicState?{ ?????//考試科目 ?????private?String?subject; ?????//考試地點 ?????private?String?location; ?????public?String?getSubject()?{ ?????????????return?subject; ?????} ?????public?void?setSubject(String?subject)?{ ?????????????this.subject?=?subject; ?????} ?????public?String?getLocation()?{ ?????????????return?location; ?????} ?????public?void?setLocation(String?location)?{ ?????????????this.location?=?location; ?????} ?????@Override ?????public?boolean?equals(Object?obj){ ?????????????if(obj?instanceof?ExtrinsicState){ ??????????????????ExtrinsicState?state?=?(ExtrinsicState)obj; ??????????????????return?state.getLocation().equals(location)?&&?state.getSubject().equals(subject); ?????????????} ?????????????return?false; ?????} ?????@Override ?????public?int?hashCode(){ ?????????????return?subject.hashCode()?+?location.hashCode(); ?????} } 注意,一定要覆寫equals和hashCode方法,否則它作為HashMap中的key值是根本沒有意義的,只有hashCode值相等,并且equals返回結果為true,兩個對象才相等,也只有在這種情況下才有可能從對象池中查找獲得對象。 注意 如果把一個對象作為Map類的鍵值,一定要確保重寫了equals和hashCode方法,否則會出現通過鍵值搜索失敗的情況,例如map.get(object)、map.contains(object)等會返回失敗的結果。 SignInfo的修改較小,僅在SignInfo中引入該ExtrinsicState外部狀態對象,在此不再贅述。我們再來看享元工廠,它是以ExtrinsicState類作為外部狀態,如代碼清單28-14所示。 代碼清單28-14 享元工廠 public?class?SignInfoFactory?{ ?????//池容器 ?????private?static?HashMap<ExtrinsicState,SignInfo>?pool?=?new?HashMap?<ExtrinsicState,SignInfo>(); ?????//從池中獲得對象 ?????public?static?SignInfo?getSignInfo(ExtrinsicState?key){ ?????????????//設置返回對象 ?????????????SignInfo?result?=?null; ?????????????//池中沒有該對象,則建立,并放入池中 ?????????????if(!pool.containsKey(key)){ ?????????????????????result?=?new?SignInfo(); ?????????????????????pool.put(key,?result); ?????????????}else{ ?????????????????????result?=?pool.get(key); ?????????????} ?????????????return?result; ?????} } 重點是看看我們的場景類,我們來測試一下性能差異,如代碼清單28-15所示。 代碼清單28-15 場景類 public?class?Client?{ ?????public?static?void?main(String[]?args)?{ ?????????????//初始化對象池 ?????????????ExtrinsicState?state1?=?new?ExtrinsicState(); ?????????????state1.setSubject("科目1"); ?????????????state1.setLocation("上海"); ?????????????SignInfoFactory.getSignInfo(state1); ?????????????ExtrinsicState?state2?=?new?ExtrinsicState(); ?????????????state2.setSubject("科目1"); ?????????????state2.setLocation("上海"); ?????????????//計算執行100萬次需要的時間 ?????????????long?currentTime?=?System.currentTimeMillis(); ?????????????for(int?i=0;i<1000000;i++){ ?????????????????????SignInfoFactory.getSignInfo(state2); ?????????????} ?????????????long?tailTime?=?System.currentTimeMillis(); ?????????????System.out.println("執行時間:"+(tailTime?-?currentTime)?+?"?ms"); ?????} } 運行結果如下所示: 執行時間:172 ms 同樣,我們看看以String類型作為外部狀態的運行情況,如代碼清單28-16所示。 代碼清單28-16 場景類 public?class?Client?{ ?????public?static?void?main(String[]?args)?{ ?????????????String?key1?=?"科目1上海"; ?????????????String?key2?=?"科目1上海"; ?????????????//初始化對象池 ?????????????SignInfoFactory.getSignInfo(key1); ?????????????//計算執行10萬次需要的時間 ?????????????long?currentTime?=?System.currentTimeMillis(); ?????????????for(int?i=0;i<10000000;i++){ ?????????????????????SignInfoFactory.getSignInfo(key2); ?????????????} ?????????????long?tailTime?=?System.currentTimeMillis(); ?????????????System.out.println("執行時間:"+(tailTime?-?currentTime)?+?"?ms"); ?????} } 運行結果如下所示: 執行時間:78 ms 看到沒?一半的效率,這還是非常簡單的享元對象,看看我們重寫的equals方法和hashCode方法,這段代碼是必須實現的,如果比較復雜,這個時間差異會更大。 各位,想想看,使用自己編寫的類作為外部狀態,必須覆寫equals方法和hashCode方法,而且執行效率還比較低,這種吃力不討好的事情最好別做,外部狀態最好以Java的基本類型作為標志,如String、int等,可以大幅地提升效率。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看