3

前の質問に基づくさらに別のシナリオ。私の意見では、その結論は、幅広い聴衆に役立つのに十分一般的です。ここからピーター・ローリーを引用:

同期はメモリバリアを使用して、ブロック内で参照されているかどうかに関係なく、すべてのメモリがそのスレッドに対して一貫した状態にあることを保証します。

まず第一に、私の問題はデータの可視性のみを扱っています。つまり、アトミック性(「操作の同期」)はソフトウェアですでに保証されているため、すべての書き込み操作は同じ値での読み取り操作の前に完了し、その逆も同様です。したがって、問題はスレッドによってキャッシュされる可能性のある値についてのみです。

2つのスレッド、threadAthreadB、および次のクラスについて考えてみます。

public class SomeClass {

private final Object mLock = new Object();    
// Note: none of the member variables are volatile.

public void operationA1() {
   ... // do "ordinary" stuff with the data and methods of SomeClass

     /* "ordinary" stuff means we don't create new Threads,
         we don't perform synchronizations, create semaphores etc.
     */
}

public void operationB() {
  synchronized(mLock) {
     ...
     // do "ordinary" stuff with the data and methods of SomeClass
  }
}

// public void dummyA() {
// synchronized(mLock) {
//    dummyOperation();
//  }
// }

public void operationA2() {
   // dummyA();  // this call is commented out

   ... // do "ordinary" stuff with the data and methods of SomeClass
}
}

既知の事実(それらは私のソフトウェアのアーキテクチャから得られます):

  • operationA1()threadAによって呼び出され、threadBoperationA2()によって呼び出さますoperationB()
  • operationB()このクラスのthreadBによって呼び出される唯一のメソッドです。同期ブロック内にあることに注意してください。operationB()
  • 非常に重要:これらの操作は、次の論理的な順序で呼び出されることが保証されています:operationA1()、、。前の操作が呼び出される前に、すべての操作が完了することが保証されています。これは、より高いレベルのアーキテクチャ同期によるものです(メッセージキューですが、現在は関係ありません)。私が言ったように、私の質問は純粋にデータの可視性に関連しています(つまり、データコピーが最新であるか、スレッドの独自のキャッシュが原因で古くなっているか)。operationB()operationA2()

Peter Lawreyの引用に基づいて、のメモリバリアは、すべてのメモリがの間operationB()、一貫した状態にあることを保証します。したがって、たとえば、threadAがの一部の値を変更した場合、これらの値は、開始時までにthreadAのキャッシュからメインメモリに書き込まれます。質問1:これは正しいですか?threadBoperationB()operationA1()operationB()

質問2operationB()メモリバリアを離れると、によって変更されたoperationB()(そして場合によってはthreadBによってキャッシュされた)値がメインメモリに書き戻されます。しかし、誰もthreadAにメインメモリとの同期を要求しなかったため、operationA2()は安全ではありませんよね?したがって、 threadAには、呼び出される前の時点からキャッシュされたコピーが残っている可能性がoperationB()あるため、の変更がメインメモリにあるかどうかは問題ではありません。operationB()

質問#3:Q.#2での疑惑が正しい場合は、ソースコードをもう一度確認してメソッドdummyA()のコメントを解除し、のdummyA()呼び出しのコメントを解除しますoperationA2()。これは他の点では悪い習慣かもしれませんが、これは違いを生むのでしょうか?私の(おそらく誤った)仮定は次のとおりです:dummyA()threadAが(同期されたブロックのために)メインメモリからキャッシュされたデータを更新するので、によって行われたすべての変更が表示されます。つまり、今ではすべてが安全です。ちなみに、メソッド呼び出しの論理的な順序は次のとおりです。mLockoperationB()

  1. operationA1()
  2. operationB()
  3. dummyA()
  4. operationA2()

私の結論:の同期ブロックによりoperationB()threadBは、以前に変更された可能性のあるデータの最新の値を確認します(例:) operationA1()。の同期ブロックによりdummyA()threadAは、で変更されたデータの最新のコピーを確認しoperationB()ます。この一連の思考に誤りはありますか?

4

1 に答える 1

2

質問2に関するあなた自身の直感は、一般的に正しいです。operationA2の開始時にsynchronized(mLock)を使用すると、メモリバリアが発行されます。これにより、操作A2による以降の読み取りで、操作Bによって実行された書き込みが確実に表示されます。これは、synchronized( mLock)操作中B。

ただし、質問1に答えるには、operationA1の最後に完全なメモリバリアを挿入しない限り、operationBはoperationA1によって実行された書き込みを認識しない可能性があることに注意してください(つまり、operationA1スレッドのキャッシュから値をフラッシュするようにシステムに指示するものはありません)。したがって、operationA1の最後にdummyAを呼び出すことができます。

完全に安全で保守しやすくするため、またこれらのメソッドの実行は互いに重複しないと述べているため、パフォーマンスを損なうことなく、共有状態のすべての操作をsynchronized(mLock)ブロックに含める必要があります。

于 2012-07-03T02:14:54.970 に答える