私はシングルトン、特にシングルトンのレイジー初期化と熱心な初期化に関するいくつかの研究を行っていました。
熱心な初期化の例:
public class Singleton
{
//initialzed during class loading
private static final Singleton INSTANCE = new Singleton();
//to prevent creating another instance of Singleton
private Singleton(){}
public static Singleton getSingleton(){
return INSTANCE;
}
}
しかし、上記のように初期化が熱心であり、スレッドの安全性はjvmに任されていますが、今は、この同じパターンを遅延初期化とともに使用したいと思います。
だから私はこのアプローチを思い付きます:
public final class Foo {
private static class FooLoader {
private static final Foo INSTANCE = new Foo();
}
private Foo() {
if (FooLoader.INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
return FooLoader.INSTANCE;
}
}
上記のように
private static final Foo INSTANCE = new Foo();
クラスFooLoaderが実際に使用される場合にのみ実行されます。これにより、遅延インスタンス化が処理され、スレッドセーフであることが保証されます。
これは正しいですか?
最初の設計は実際には怠け者です。考えてみてください。インスタンスは、クラスが初期化されたときにのみ作成されます。クラスはgetSingleton()
メソッドが呼び出されたときにのみ初期化されます[1]。したがって、インスタンスは要求されたときにのみ作成されます。つまり、遅延作成されます。
[1] http://docs.Oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1
2番目は読みやすさの点で非常に悪く、1番目は適切です。これをご覧ください 記事 。ダブルチェックロックについてですが、シングルトンマルチスレッドに関する幅広い情報も提供します。
最良の方法は、実際には Enum Way を使用することです。
public enum Singleton {
INSTANCE;
public void execute (String arg) {
//... perform operation here ...
}
}
私の意見では、これは不適切なパターンです。 JVMの振る舞いについての仮定を行いますが、これは簡単ではなく混乱を招くものです。また、ダミークラスがあります。可能な場合、ダミークラスは避けてください。
簡単なアプローチをお勧めします。
public class Foo {
private volatile static final Foo instance = null;
private Foo() {
}
public static Foo instance() {
if (instance == null) instance = new Foo();
return instance;
}
}
}
...ただし、これはそのままでは機能しません。スレッドセーフではありません。本当に必要なのは、BlochのItem 71に記載されているダブルチェックパターンですEffective Java; here を参照してください。あなたのケースへのリンクで例を適用すると、次のようになります:
public class Foo {
private volatile static final Foo instance = null;
private Foo() {
}
public static Foo instance() {
if (instance != null) return instance;
synchronized(instance) {
Foo result = instance;
if (instance == null) {
result = instance = new Foo();
return result;
}
}
}
ノート: