5

コード スニペット - 1

class RequestObject implements Runnable
{
    private static Integer nRequests = 0;

    @Override
    public void run()
    {       
        synchronized (nRequests)
        {
            nRequests++;
        }
    }
}

コード スニペット - 2

class RequestObject implements Runnable
{
    private static Integer nRequests = 0;
    private static Object lock = new Object();

    @Override
    public void run()
    {       
        synchronized (lock)
        {
            nRequests++;
        }
    }
}

2 番目のコード スニペットは競合状態を引き起こすことなく正常に動作していますが、最初のコード スニペットは、同じクラス (RequestObject) の異なるインスタンス間で静的データ メンバーへのアクセスを同期することに成功していません。誰かがこれにもっと光を当てることができますか. 最初のアプローチが機能しない理由を理解したいと思います。

私の元の実装は最初のものです。後でhttps://stackoverflow.com/a/2120409/134387で見ました。

4

3 に答える 3

6

常に新しい Integer オブジェクトを作成し、それを同期します。これは、少なくともそれについて考えるのを非常に混乱させます。したがって、次のシナリオを取得できます。

スレッド A は現在の値nRequests(0 としましょう) を取得します。

同じ値 (0) のスレッド B キュー

スレッド A が増加nRequestsします (値 1 まで)

スレッド C は新しい値を取得して同期し、値を増やして値を手放します。

スレッド A は 0 でモニターを手放します

スレッド B は 0 で同期し、それを 1 に増やして、C の変更を上書きします。

2 番目のアプローチでは、全員が同期する必要がある単一のオブジェクトがあります。これはまさにあなたが望むものです。

于 2013-08-03T07:42:58.760 に答える
5

のインスタンスIntegerは不変であるnRequests++ため、新しいIntegerオブジェクトを作成して結果を保持し、それを に格納しnRequestsます。synchronizedステートメントはオブジェクトで同期します。したがって、スレッドは異なるオブジェクトで同期します。はい、同じオブジェクトの同期ブロックに同時にスレッドが 1 つしか存在しない場合がありますが、異なるオブジェクトの同期ブロックに異なるスレッドが同時に存在する可能性があります ...

静的状態へのアクセスを同期する最も簡単な方法は、静的同期メソッドに配置することです。

static synchronized void increment() {
    nRequests++;
}

これは、次の同期ブロックと同等です。

synchronized (RequestObject.class) {
    nRequests++;
}

ここで、RequestObject は static フィールドを含むクラスです。

于 2013-08-03T07:40:10.243 に答える
2

問題は、Integerクラスが Java で不変であることです。したがって、nRequests++呼び出しごとに新しいオブジェクトが作成されるため、すべてのスレッドは異なるオブジェクトで同期します。

2 番目のケースでは、オブジェクトはすべてのインスタンスで同じであり、変数lockへのスレッドのアクセスを正常にシリアル化します。nRequests

于 2013-08-03T07:42:15.817 に答える