1

遅延初期化と、作成がスレッドセーフである必要のないオブジェクトの格納用に設計されたクラスがあります。コードは次のとおりです。

class SyncTest {
    private static final Object NOT_INITIALIZED = new Object();
    private Object object;

    /**
     * It's guaranteed by outer code that creation of this object is thread safe
     * */
    public SyncTest() {
       object = NOT_INITIALIZED;
    }

    public Object getObject() {
        if (object == NOT_INITIALIZED) {
            synchronized (NOT_INITIALIZED) {
                if (object == NOT_INITIALIZED) {
                    final Object tmpRef = createObject();
                    object = tmpRef;
                }
            }
        }
        return object;
    }

    /**
     * Creates some object which initialization is not thread safe
     * @return required object or NOT_INITIALIZED
     * */
    private Object createObject() {
        //do some work here
    }
}

ここで、final変数tmpRefは、作成されたオブジェクトを格納してから、チェックされた変数に割り当てるために使用されますobject。これはテストでは機能しますが、確実に正しいとは言えず、コンパイラによって最適化されません。このアプローチを使用できますか、またはobjectフィールドを次のように宣言する必要がありますvolatileか?

また、ラッパー クラスを使用したバリアントは、行が

final Object tmpRef = createObject();

これに置き換える必要があります:

Object tmpRef = new FinalWrapper(createObject()).getVal();

ラッパー クラスは次のようになります。

private class FinalWrapper {
    private final Object val;

    public FinalWrapper(Object val) {
        this.val = val;
    }

    public Object getVal() {
        return val;
    }
}

この例のいくつかは、マルチスレッド環境で安全に使用できますか (特に final ローカル フィールドを持つバリアント)?

4

2 に答える 2

2
object = NOT_INITIALIZED;

これを通常の怠惰なシングルトンの問題を回避するトリックとして想像すると、単純に

object = null;

それは正しくありません。あなたのトリックはスレッドセーフを勝ち取りませんでした。volatile遅延初期化されたオブジェクトを指す変数を使用して、標準のダブルチェックイディオムを打ち負かすことはできません。nullしたがって、余分な複雑さ、 use 、および useを取り除くことをお勧めしますvolatile

コメントへの回答:

final フィールドのみを使用した calss 初期化が常にスレッドセーフであることは、JMM によって保証されています。

クラスの初期化は、フィールドの種類に関係なく、常にスレッドセーフです。クラスを使用するたびに、静的フィールドによって参照されるオブジェクトが、少なくともすべてのクラスの初期化コードが完了した時点と同じくらい最新であることが保証されます。

ローカル最終フィールドにも同じことが当てはまりますか?

final フィールドを逆参照することによって到達するオブジェクトは、少なくとも final フィールドを含むオブジェクトのコンストラクターが完了した時点と同じくらい最新になります。ただし、ソリューションでは、フィールドを逆参照することさえありません。その値を確認するだけです。これは、定数nullの値が等しいかどうかをチェックすることと厳密に同じです。NOT_INITIALIZED

于 2015-08-17T08:29:27.233 に答える
1

スレッド セーフを保証するobjectために変数をマークする必要があります。また、このパターンはJava 1.5 以降でのみ安全であることに注意してください。volatile

Joshua Bloch の言葉を引用すると、これはトリッキーなコードです。

このイディオムは非常に高速ですが、複雑でデリケートでもあるため、いかなる方法でも変更しようとしないでください。コピーして貼り付けるだけです-通常は良い考えではありませんが、ここでは適切です

于 2015-08-17T08:09:24.103 に答える