3

二重チェックのロック修正が機能しない方法についてはすべて読んだことがあり、遅延初期化は好きではありません。

これが私の例です: private int timesSafelyGotten = 0; プライベート ヘルパー ヘルパー = null;

public getHelper()
{
    if (timesSafelyGotten < 1) {
        synchronized (this) {
            if (helper == null) {
                helper = new Helper();
            } else {
                timesSafelyGotten++;
            }
        }
    }
    return helper;
}

このように、同期コードはヘルパーを作成するために 1 回実行し、最初に取得するときに 1 回実行する必要があるため、理論的には、ヘルパーを作成した同期コードがロックを解放し、ヘルパーの初期化が完了するまで、 timesSafelyGotten をインクリメントすることはできません。

問題はないと思いますが、あまりにも単純すぎて、本当であるとは思えません。どう思いますか?

カレブ・ジェームズ・デリスル

4

3 に答える 3

4

メモリ バリア ( synchronizedvolatile、または の同等物 java.util.concurrent) がないと、スレッドは、別のスレッドのアクションがソース コードに表示される順序とは異なる順序で発生する可能性があります。

の読み取りにはメモリ バリアがないため、が割り当てられる前にインクリメントtimesSafelyGottenされる別のスレッドに表示される可能性があります。その結果、メソッドから返されます。timesSafelyGotten helpernull

実際には、これは単体テスト中に多くのアーキテクチャで機能する可能性があります。しかし、それは正しくなく、最終的にはどこかで失敗します。

二重チェックのロック今でも機能しますが、正しく実装するのは難しく、かなりの費用がかかります。壊れにくく、読みやすく、風変わりなものを必要としない遅延初期化のパターンがあります。

于 2009-12-10T06:41:29.467 に答える
2

JDK5+ を使用している場合は、java.util.concurrent を使用します。この場合、おそらくAtomicIntegerを使用します。

これらのユーティリティが提供されているのは、低レベルのスレッド同期プリミティブを適切に機能させるほど十分に理解している人は誰もいないと予想されるためです。

于 2009-12-10T05:28:54.557 に答える
1

それは良いことではありません。timeSafelyGotten > 1 を取得できます。例:

  1. スレッド 1 は成功したかどうかを確認し、同期ラインで停止します
  2. Thread2 は成功したかどうかをチェックし、同期コードで停止します。
  3. スレッド 3 は、成功したかどうかを確認し、同期コードで停止します。
  4. Thread1 は同期ブロックに入り、ヘルパーを作成してこのブロックを離れます。
  5. Thread2 は同期ブロックに入り、timeSafelyGotten をインクリメントして、このブロックを離れます。
  6. Thread3 は同期ブロックに入り、timeSafelyGotten をインクリメントして、このブロックを離れます。

したがって、timeSafelyGotten = 2 です。

もう 1 つチェックを追加する必要があります。

if (helper == null) {
    helper = new Helper();
} else if (timesSafelyGotten < 1) {
    timesSafelyGotten++;
}

または同期を上に移動します。

synchronized(this) {
   if (timeSafelyGotten < 1) {
       ...
   }
}

最初の方法は、毎回同期を使用しないため、優れています。

もう 1 つのヒント: synchronize( this ) を使用しないでください。誰かがあなたのオブジェクトを同期に使用する可能性もあります。内部同期には特別なプライベート オブジェクトを使用します。

classs MyClass {
    private Object syncRoot = new Object();

    ...
    synchronized(syncRoot) {
        ....
    }
}
于 2009-12-10T05:35:29.497 に答える