選單
GSS 技術部落格
在這個園地裡我們將從技術、專案管理、客戶對談面和大家分享我們多年的經驗,希望大家不管是喜歡或是有意見,都可以回饋給我們,讓我們有機會和大家對話並一起成長!
若有任何問題請來信:gss_crm@gss.com.tw
3 分鐘閱讀時間 (553 個字)

Java Concurrency #5 - Sharing Objects[2]

shutterstock_198004562
  • 公開物件(Publishing)
    • 公開一個物件指的是讓物件本身可以被外界存取,像是:
      • 物件參考指向該物件
      • 透過方法回傳該物件
      • 以參數的形式傳進方法
    • 大部份時候,我們都希望類別的內部實作可以確實被封裝,只透過公開的方法來操作那些內部細節(internals),然而有的時候我們會需要公開它的 internals
      • 這麼做會破壞封裝,且可能確保 invariant 變得相對困難
      • 公開物件必須透過同步(synchronization)來確保執行緒安全
      • 物件在不該被開公開時卻公開了,稱作物件逃走中(escaped)
    • 公開物件最要不得的方式之一就是透過 public static field
      • 如下方程式碼所示:
          //...(略)
          public static Set<Secret> knownSecrets;
          public void initialize() {
              knownSecrets = new HashSet<>();
          }
        • 這種寫法,任何類別或執行緒都可以看到其內容
        • 公開物件除了物件本身有風險之外,也會間接影響到其他物件(e.g. 在上面的 Set 裡放了物件,那麼那個物件也會被存取)
      • 同樣的,透過方法回傳參考也是一樣的效果:
          class UnsafeStates {
              private String[] states = new String[] {"AK", "AL"  };
              public String[] getStates() {
                  return states;
              }
          }
        • 即便 states 是 private,public method 還是會使其內容暴露在外,任何物件都可以修改其內容
        • 承上,states 原本應被封裝,但因為 public method 而逃跑了(escape)
      • 最後一種比較不明顯,是公開內部類別的實作:
          public class ThisEscape {
              public ThisEscape(EventSource source) {
                  source.registerListener(new EventListener() {
                      public void onEvent(Event e) {
                          doSomething(e);
                      }
                  });
              }
          }
        • ThisEscape 被建構的過程中, EventListener 被公開了,造成 ThisEscape 也被公開了
        • 這是因為內部類別預設會有外部類別的參考(不知道的請看 Thinking in Java)
        • 如果一個物件在建構子中公開物件(在此是內部類),那建構子所建構的物件就不會處在一個穩定一致的狀態
          • 一個比較常見的錯誤是在建構子中開新的 Thread
          • 基本上開新 Thread 本身並沒有什麼問題,但如果又馬上呼叫 start 就會有上述的情形發生了
      • 承上例,避免 this 被內部類別公開的方式如下:
          public class SafeListener {
              private final EventListener listener;
              private SafeListener() {
                  listener = new EventListener() {
                      public void onEvent(Event e) {
                          doSomething(e);
                      }
                  };
              }
              public static SafeListener newInstance(EventSource source) {
                  SafeListener safe = new SafeListener();
                  source.registerListener(safe.listener);
                  return safe;
              }
          }
        • 留意到建構子為 private 且只能透過靜態工廠取得物件實例
        • 如此便能避免物件在建構過程中逃跑了
Java Concurrency #6 - Thread Confinement
Java Concurrency #4 - Sharing Objects[1]

相關文章

 

評論

尚無評論
已經注冊了? 這裡登入
Guest
2024/05/06, 週一

Captcha 圖像