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

Java Concurrency #8 - Safe Publification

shutterstock_198004562
  • 目前為止我們對於物件的公開都採取以下的手段:
    • 封裝它
      • 透過私有建構子 + 靜態工廠(詳見第5篇)
    • 把它限制在該執行緒內
      • ThreadLocal(詳見第6篇)
    • 把它封裝另一個物件內
      • Immutable holder class(詳見第7篇)
  • 如果想透過用一個 Holder 包裝呢?
    • 如以下範例:
        // ...(略)
        // Unsafe publication
        public Holder holder;
        public void initialize() {
            holder = new Holder(42);
        }
      • 也許你會覺得奇怪,不是已經把 state 都藏在 Holder 裡了嗎?
      • 這是因為 visibility 的關係,多個執行緒可能會造成 Holder 的實例建立不完全(物件建立的流程請參閱Thinking In Java)
    • 承上例,你無法仰賴未建構完全的物件實例
      • 一個執行緒可能會看到 holder 物件的值為 null,在下一毫秒又突然變成包含 42 的實例,即使過程中都沒看到 holder 被修改
      • 驗證可能會發生狀態不一致的程式如下:
          public class Holder {
              private int n;
              public Holder(int n) {
                  this.n = n;
              }
              public void assertSanity() {
                  if (n != n)
                      throw new AssertionError("This statement is false.");
              }
          }
        • 如果 Holder 在一個執行緒中被公開了,另一個執行緒呼叫 assertSanity 是有機會拋出 Error 的
        • 因為並沒有使用同步的機制公開Holder,因此這是一個不安全的公開物的方式
    • 由上面可知 Immutable objects 很重要
      • 究竟有多麼重要呢?重要到 Java Memory Model 特別保證其初始化是絕對執行緒安全的,因此共享沒問題
      • 前面很多範例中我們知道物件參考在別的執行緒可看見,並不代表它的 state 也是可看見的,要確保這件事必須要同步
      • Immutable object 不需要同步也能確保執行緒安全,而為了讓那個特別保證能成立,以下的條件是必然不可違反的:
        • state 無法被修改
        • 所有的 field 都加上 final
        • 正確地建立物件(上面的 Holder 就有機會不被正確建立)
      • 特別留意一件事,final field 指向的如果是正確建立的物件,那就不需要同步,但如果指向的是 mutable object,那同步仍然是需要的(final field 只是代表參考不能再指向別的物件,不代表那個物件本身的 state 不會變)
    • 如何安全地公開物件?
      • 物件參考與其指向的物件都必須是可見的(Visible),這可以透過以下幾個方法達成:
        • 在靜態初始化(static initializer)物件參考
        • volatile 的參考指向物件,或將物件放在 AtomicReference
        • 將物件放在一個被正確建立final field 中
        • 將物件的存取都以 lock 的機制保護住
    •  執行緒安全集合的內在同步(Internal synchronization)
      •  指的是如果執行緒 A 把某個物件 X 放到集合中,執行緒 B 看到的 X 會如執行緒 A 當初放得那樣,但這件事並沒有顯而易見的同步機制保護著
        •  在這種情況下,這些集合的實作是符合前述的安全公開物件方法,例如 java.util.Vector 或是 Collections.synchronizedList
      •  即使 Javadoc 本身並沒有明確說明,執行緒安全集合的 library 提供如下的服務:
        •  Hashtable, synchronizedMap, ConcurrentMap 是安全公開放置在其中的 key/value
        •  Vector, CopyOnWriteArrayList, CopyOnWriteArraySet 是安全公開放置在其中的物件
        •  BlockingQueue, ConcurrentLinkedQueue 是安全公開放值在佇列中的物件
      •  其他的機制像是 Future 或是 Exchanger 後緒會再提到
      •  靜態初始化物件是最簡單的方式
        •  例如 public static Holder holder = new Holder(42)
        •  這是因為靜態初始化是由 JVM 內建的同步機制所保護的,所以以這種方式初始化的物件其公開都是安全的
    •  物件公開方式依其可變性(mutability)決定
      • immutable object 怎樣公開都行
      • effectively immutable object 必須被安全地公開
        • effectively immutable 指的是它不是 immutable 物件,但它的 state 不會再被修改
        • 安全地公開指的是透過前面的方法保護之
      • mutable object 要透過 lock 機制使其執行緒安全
    • 當你取得一個物件時,要先了解可以怎麼用它,如果物件是:
      • Thread Confined - 那怎麼用都沒差,因為只有該執行緒可以對其存取
      • Shared, readonly - 也是怎樣用都可以,這類物件有 immutable objecteffectively immutable object
      • Shared, thread-safe - 執行緒安全做在內部,所以只要透過其公開的介面操作即可
      • Guarded - 需要有 lock 才能對其存取
Java Concurrency #9 - Composing Objects
Java Concurrency #7 - Immutability

相關文章

 

評論

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

Captcha 圖像