2013-08-03 7 views
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で見た。

+3

「整数」は不変であるためです。インクリメントすることはあなたの考えをしていません –

+1

も整数を使用せず、 'int'を使用してください –

+0

これは別の質問です。新しい質問にしてください。 @JensSchauder、完了。 –

答えて

6

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

スレッドAはnRequestsの現在値のホールドを取得します(0を言うことができます)同じ値の

スレッドBキュー(0)

スレッドAが増加nRequestsを(値1へ)

スレッドCは、新しい値を保持して同期し、値を増やします。

スレッドAは、Bが0に同期し、あなたは誰もがしていることを、単一のオブジェクトを持っている第二のアプローチではC

の変更を上書きし、これを1に増加0

スレッドにモニターの行くことができますオンに同期する。それはまさにあなたが望むものです。

4

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

アクセスを同期する最も簡単な方法静的な状態は、静的同期方法でそれを入れている:

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

これは、次の同期ブロックと等価である:

RequestObjectが静的フィールドを含むクラスである
synchronized (RequestObject.class) { 
    nRequests++; 
} 

2

問題は、Integerクラスがjavaで不変であることです。 nRequests++は各呼び出しで新しいオブジェクトを作成するため、各スレッドは異なるオブジェクトで同期します。

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

関連する問題