web-dev-qa-db-ja.com

遅延読み込みとスレッドセーフの組み合わせによるシングルトンパターン

私はシングルトン、特にシングルトンのレイジー初期化と熱心な初期化に関するいくつかの研究を行っていました。

熱心な初期化の例:

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が実際に使用される場合にのみ実行されます。これにより、遅延インスタンス化が処理され、スレッドセーフであることが保証されます。

これは正しいですか?

22
tuntun fdg

2番目のコードスニペットは、私の意見では、シングルトンを遅延的に初期化するスレッドセーフの最良の方法です。実際にはパターン名があります

要求時の初期化ホルダーのイディオム

使用することをお勧めします。

21
John Vint

最初の設計は実際には怠け者です。考えてみてください。インスタンスは、クラスが初期化されたときにのみ作成されます。クラスはgetSingleton()メソッドが呼び出されたときにのみ初期化されます[1]。したがって、インスタンスは要求されたときにのみ作成されます。つまり、遅延作成されます。

[1] http://docs.Oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1

6
ZhongYu

2番目は読みやすさの点で非常に悪く、1番目は適切です。これをご覧ください 記事 。ダブルチェックロックについてですが、シングルトンマルチスレッドに関する幅広い情報も提供します。

2
Mikhail

最良の方法は、実際には Enum Way を使用することです。

public enum Singleton {
    INSTANCE;
    public void execute (String arg) {
            //... perform operation here ...
    }
}
1
Jean Logeart

私の意見では、これは不適切なパターンです。 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;
        }
    }
}

ノート:

  • このコードのパフォーマンスについて心配する必要はありません。最新のJVMがそれを処理し、問題ありません。結局、 時期尚早の最適化はすべての悪の根源です
  • 他の回答で示唆されているように、上記はBlochの好ましい解決策ではありませんが、シングルトンにenumを使用することはOPが元々やったように意味的に不適切だと思います。
0
einpoklum