2

ウィキペディアにリストされている Java でのシングルトンの実装の 1 つがあります。

public class SingletonDemo {
    private static volatile SingletonDemo instance = null;

    private SingletonDemo() {
    }

    public static SingletonDemo getInstance() {
        if (instance == null) {
            synchronized (SingletonDemo.class) {
                if (instance == null) {
                    instance = new SingletonDemo();
                }
            }
        }
        return instance;
    }
}

Java 言語仕様 17 のパラグラフ 5 に次のように記載されています。

コンストラクターが終了すると、オブジェクトは完全に初期化されたと見なされます。オブジェクトが完全に初期化された後にのみオブジェクトへの参照を確認できるスレッドは、そのオブジェクトの final フィールドの正しく初期化された値を確認できることが保証されます。

では、SingletonDemo クラスに非 final フィールドがあるとします。したがって、並行スレッドは、コンストラクターで指定された正しい値ではなく、デフォルト値を読み取ることができますか?

4

4 に答える 4

5

It is possible to implement double-checked locking (DCL) correctly in Java 5 and later. In Java 4 and earlier, it is not possible due to the fact that the behaviour of volatile with respect to synchronization was not properly specified (and in practice was inadequate).

The code you included in your question is a correct implementation for DCL ... when run using a Java 5 JRE or later.

But (IMO), it is not worth using DCL. Especially if you (or developers coming after you) don't completely understand how to do it if correctly / safely.

The performance benefit is simply too small for this to be a worthwhile optimization in a real-life Java application. (And if it is, you are probably overusing / misusing singletons ... and that will bite you in other ways!)


Ok, so imagine that our SingletonDemo class has non-final field. So, concurrent thread will be able to read default value instead of correct value specified in constructor?

(The quoted JLS text is about an entirely different situation. It is about final fields. It is not relevant here. And you cannot infer the behaviour for non-final fields with synchronization from the behaviour of final fields without synchronization.)

The answer to your question is No. The code in your question is doing enough to guarantee that a concurrent thread won't see the default value. To understand why, read the following:

  • all of Section 17.4 of the JLS, and
  • the last chapter of Goetz et al "Java Concurrency in Practice", which includes a section on DCL (if I remember correctly ...)
于 2014-01-27T06:25:08.037 に答える
4

あなたの引用は次のように述べています。

最終フィールドとコンストラクターが終了した場合、スレッドは初期化された値を確認できます。

それは言わない

final フィールドではない場合、スレッドは初期化された値を見ることができません。

volatile のセマンティクスは、その例でも安全な公開を保証します。

また、DCL は非常に便利だとおっしゃっています。ほぼすべての状況で、複雑でエラーが発生しやすい構成を使用する必要のない、より良い方法があると思います。優先順:

  • シングルトンをまったく使用しない
  • 列挙型を使用する
  • 初期化オンデマンドホルダーイディオムを使用する
于 2014-01-27T07:27:50.920 に答える