我有一个我需要使用的库,该库具有静态值的危险初始化(示例中的类已精简到最小):

public TheirBaseClass {
    public static String PathToUse = null;

    public BaseClass(){
        PathToUse = "Configured";

        // ...
        // do some other stuff with other side effects
        // ...
    }
}


我遇到一种情况,我尝试从值ConfigValue读取而不实例化该类(以避免某些副作用)。

Paths.get(TheirBaseClass.PathToUse).toFile()....


这导致NullPointerException

因为需要使用此类,所以我希望从该类继承,并尝试采取措施以确保在访问静态对象时进行了初始化。

public MyBaseClass extends TheirBaseClass{

    private static final AtomicBoolean isInitialized = new AtomicBoolean(false);

    static {
        MyBaseClass.Initialize();
    }

    public static void Initialize(){
        // FindBugs does not like me synchronizing on a concurrent object
        synchronized(isInitialized){
            if( isInitialized.get() ){
                return;
            }
            new TheirBaseClass();
            isInitialized.set(true);
        }
    }

    public MyBaseClass(){
        super();
    }

}


这让我能够

MyBaseClass.Initialize();
Paths.get(MyBaseClass.PathToUse).toFile()....


这似乎运行得很好(并解决了我们一直存在的其他一些幻像缺陷)。它允许TheirBaseClass自然运行,同时允许我在可能需要的几种情况下安全地强制初始化。

但是,当我针对此代码运行FindBugs时,得到JLM_JSR166_UTILCONCURRENT_MONITORENTER。阅读说明后,我同意使用AtomicBoolean是危险的,因为其他人可以更改该值,但是...


我认为在这种情况下可以忽略(但是有疑问)
我通常更喜欢重写代码,而不是放置忽略标记


我实际上是在做危险的事情(只是看不到)吗?有一个更好的方法吗?

不幸的是,不能使用其他TheirBaseClass

有关


Are Java static initializers thread safe?

最佳答案

您可能会发现更容易适应lazy holder idiom

public MyBaseClass {
  private static class Initializer {
    static {
      new TheirBaseClass();
    }

    // Doesn't actually do anything; merely provides an expression
    // to cause the Initializer class to be loaded.
    private static void ensureInitialized() {}
  }

  {
    Initializer.ensureInitialized();
  }

  // Rest of the class.
}


这利用了这样的事实,即类加载仅发生一次并被同步(在单个类加载器内)。它仅在实例化MyBaseClass时发生。

10-04 20:39