如何保证课堂加载

时间:2015-01-13 23:35:53

标签: java

我有一个类,其初始化需要相当多的时间;它调用服务器,该服务器需要几分钟才能准备就绪。

该类的方法不会被调用很长时间,并且它们总是从启动时自动加载的类中调用。我的设置是这样的:

class SlowToStartUp {
  public static void init() {
     // do nothing
  }

  static {
    initiateConnectionToServer()
  }

  public static V invokeServer() {
     waitForServerToConnect();
     return valueFromServer();
  }
}


class AlwaysLoaded {
  static {
     SlowToStartUp.init();
  }

  public void someMethod() {
     V v = SlowToStartUp.invokeServer();
  }

这让我觉得结构正确。如果根本没有init()函数,那么在initiateConnectionToServer()第一次需要该类之前不会调用someMethod(),然后就会有一个不必要的(在我的系统中,不可接受的延迟。

如果我将initiateConnectionToServer()电话放在init()中,接口会更脆弱(因为电话可能会被遗忘)。

但现在我想知道自己是否已经超越了自己。编译器可以看到init()为空。它不仅可以优化呼叫吗?它现在不这样做,但它有保证吗?

我尝试将init()标记为volatile,但这是不允许的。

我正在考虑将实际的初始化放入init(),确保它是幂等的,并从静态块调用它,只是为了安全起见,但我想我会先征求意见。 / p>

3 个答案:

答案 0 :(得分:3)

一种替代方法是重构单例类而不是一堆静态方法。然后将在启动时创建单例,并且您的初始化代码将立即运行。

public class SlowPokeSingleton {
  private SlowPokeSingleton() { /* execute init code */ }

  // created at startup
  private final static SlowPokeSingleton instance = new SlowPokeSingleton();

  public static SlowPokeSingleton instance() { return instance; }
}

您需要调用instance()以确保实际创建实例。您可以将其添加到服务器启动中以确保安全。

答案 1 :(得分:0)

我对自己的问题有一个微妙的答案。触发类初始化的事件在JLS 12.4.1中指定。

  

类或接口类型T将在第一次出现以下任何一个之前立即初始化:

     
      
  • T是一个类,创建了一个T实例。
  •   
  • T是一个类,调用T声明的静态方法
  •   
  • 分配由T声明的静态字段。
  •   
  • 使用由T声明的静态字段,该字段不是常量变量(§4.12.4)。
  •   
  • T是顶级类,并且执行词法嵌套在T中的断言语句(第14.10节)。
  •   

在我看来,优化掉空静态函数的编译器会违反我用粗体标记的规定。

欢迎评论。

答案 2 :(得分:0)

我同意Giovanni Botta的说法:

  

您可以使用单例模式而不是静态方法和获取   应用程序启动后立即执行单例实例。那会   强制JVM创建实例,从而运行初始化   码。 - Giovanni Botta

具体做法是:

1)将初始化的“耗时”部分放入私有的静态“init()”方法中。

2)让你的班级的构造函数“私有”。

3)代替构造函数,提供一个公共静态“getInstance()”方法来获取对(已经初始化的)单例实例的引用。

4)如果您愿意,您的其他方法可以是非静态的。