web-dev-qa-db-ja.com

不正確な遅延初期化

Findbugは、間違った遅延初期化を使用していると言った。

public static Object getInstance() {
    if (instance != null) {
        return instance;
    }

    instance = new Object();
    return instance;
}

ここには何も問題はありません。 findbugの間違った動作ですか、何か見落としていましたか?

53

Findbugは潜在的なスレッドの問題を参照しています。マルチスレッド環境では、現在のコードでシングルトンが複数回作成される可能性があります。

多くの読み物があります ここ ですが、説明に役立ちます。

ここでの競合状態はif checkにあります。最初の呼び出しで、スレッドはif checkに入り、インスタンスを作成して「インスタンス」に割り当てます。ただし、if checkとインスタンスの作成/割り当ての間で別のスレッドがアクティブになる可能性があります。割り当てがまだ行われていないため、このスレッドはif checkを渡すこともできます。したがって、2つ(または、より多くのスレッドが入った場合はそれ以上)のインスタンスが作成され、スレッドは異なるオブジェクトへの参照を持ちます。

60

あなたのコードは必要以上にやや複雑であるため、混乱している可能性があります。

編集:それは間違いなく他の人が投稿したスレッドの問題ですが、以下の参照のためにここにダブルロックチェックの実装を投稿すると思いました:

private static final Object lock = new Object();
private static volatile Object instance; // must be declared volatile

public static Object getInstance() {
    if (instance == null) { // avoid sync penalty if we can
        synchronized (lock) { // declare a private static Object to use for mutex
            if (instance == null) {  // have to do this inside the sync
                instance = new Object();
            }
        }
    }

    return instance;
}
18
JohnKlehm

[〜#〜] note [〜#〜]:JohnKlehmのダブルロックチェックソリューションの方が優れています。歴史的な理由でこの回答をここに残します。

実際にあるはずです

public synchronized static Object getInstance() {
    if (instance == null) {
        instance = new Object();
    }

    return instance;
}
11
Varun Achar

これを正しくするには、インスタンス化をロックする必要があります

LI:静的フィールドの誤った遅延初期化(LI_LAZY_INIT_STATIC)

このメソッドには、不揮発性静的フィールドの非同期の遅延初期化が含まれています。コンパイラまたはプロセッサは命令を並べ替えるため、メソッドが複数のスレッドから呼び出される場合、スレッドは完全に初期化されたオブジェクトを参照することを保証されません。フィールドを揮発性にして問題を修正できます。詳細については、Java Memory Model Webサイトを参照してください。

4
antlersoft

マルチスレッドの問題を見逃した、

private static Object instance;

public static synchronized Object getInstance() {
    return (instance != null ? instance : (instance = new Object()));
}
2
Nilesh

サンプルを投稿してくれたJohn Klehmに感謝

また、同期ブロック内のオブジェクトインスタンスを直接割り当てようとする場合があります

synchronized (MyCurrentClass.myLock=new Object())

つまり.

private static volatile Object myLock = new Object();

public static Object getInstance() {
    if (instance == null) { // avoid sync penalty if we can
        synchronized (MyCurrentClass.myLock**=new Object()**) { // declare a private static Object to use for mutex
            if (instance == null) {  // have to do this inside the sync
                instance = new Object();
            }
        }
    }

    return instance;

}
2
jdeveloper

静的オブジェクトは同期されません。さらに、メソッドは遅延初期化ではありません。通常は、オブジェクトのマップを保持し、必要に応じて目的のマップを初期化します。したがって、必要な(呼び出される)ときに呼び出すのではなく、最初にすべてを初期化するわけではありません。

0
Shahriar

1.5以降:インスタンスは揮発性であり、youldはtmp変数を統合して、作成されたが初期化がまだ完了していないインスタンスを使用しないようにします。

private static volatile Object myLock = new Object();
private static volatile Object instance;

public static Object getInstance() {
    if (instance == null) {
        Object tmpObj;
        synchronized (myLock) {
            tmpObj = instance;
            if (tmpObj == null) {
                tmpObj = new Object();
            }
        }
        instance = tmpObj;
    }

    return instance;

}
0
user8517281