簡體   English   中英

靜態初始化非最終靜態字段是否安全?

[英]Is static initialization of non-final static fields safe?

請考慮以下代碼:

public class Text {
  private static ThreadLocal<CharsetEncoder> encoderFactory =
    new ThreadLocal<CharsetEncoder>() {
      @Override
      protected CharsetEncoder initialValue() {
        return Charset.forName("UTF-8").newEncoder().
            onMalformedInput(CodingErrorAction.REPORT).
            onUnmappableCharacter(CodingErrorAction.REPORT);
      }
    };

  public static ByteBuffer encode(String string, boolean replace)
      throws CharacterCodingException {
    CharsetEncoder encoder = encoderFactory.get();
    ...
  }
}

訪問encode() encoderFactory的行是否會在並發情況下拋出NullPointerException

是的,我很清楚,在這種情況下, encoderFactory很容易被聲明為final,這將使這個問題有點沒有實際意義。

但是,我感興趣的是,上面編寫的代碼是否仍然安全地發布了encoderFactory 如果我理解JLS 12.4 ,那應該是這樣的。 靜態初始化的步驟似乎沒有留下任何線程一旦看到類初始化就會看到處於未初始化狀態的靜態字段(即之前沒有發生)的可能性。 我認為JLS使得靜態初始化形成內存屏障變得相當清楚。

顯然已經觀察到這樣的NullPointerException ,我們最終通過使這個字段最終來修復它。 雖然這當然是一件好事,但我仍然感到困惑的是如何看到一個帶有這種模式的空指針,否則會出現一個更大的問題,因為它可能意味着非最終靜態字段的任何初始分配可能不會是可見的。

如果靜態初始化提供內存屏障的假設是可靠的(我相信它是),我想這肯定會指向一個JDK錯誤呢? 你能想到除了JDK bug之外可能發生的原因嗎?

根據12.4.2 ,它將是線程安全的:

由於Java編程語言是多線程的,因此初始化類或接口需要仔細同步。

在並發情況下,類的初始化將是安全的。 在任何線程有機會調用encode之前,將加載字段,無論字段是否被聲明為final。 使用的引用類型沒有區別(包括ThreadLocal )。

他們進一步解釋了初始化發生的確切步驟。 在初始化成功或突然完成(通過拋出異常,導致ExceptionInInitializerError )之前,不會通知線程。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM