我有一个类,其初始化需要相当多的时间;它调用服务器,该服务器需要几分钟才能准备就绪。
该类的方法不会被调用很长时间,并且它们总是从启动时自动加载的类中调用。我的设置是这样的:
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>
答案 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)如果您愿意,您的其他方法可以是非静态的。