前の質問に基づくさらに別のシナリオ。私の意見では、その結論は、幅広い聴衆に役立つのに十分一般的です。ここからピーター・ローリーを引用:
同期はメモリバリアを使用して、ブロック内で参照されているかどうかに関係なく、すべてのメモリがそのスレッドに対して一貫した状態にあることを保証します。
まず第一に、私の問題はデータの可視性のみを扱っています。つまり、アトミック性(「操作の同期」)はソフトウェアですでに保証されているため、すべての書き込み操作は同じ値での読み取り操作の前に完了し、その逆も同様です。したがって、問題はスレッドによってキャッシュされる可能性のある値についてのみです。
2つのスレッド、threadAとthreadB、および次のクラスについて考えてみます。
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:これは正しいですか?threadB
operationB()
operationA1()
operationB()
質問2:operationB()
メモリバリアを離れると、によって変更されたoperationB()
(そして場合によってはthreadBによってキャッシュされた)値がメインメモリに書き戻されます。しかし、誰もthreadAにメインメモリとの同期を要求しなかったため、operationA2()は安全ではありませんよね?したがって、 threadAには、呼び出される前の時点からキャッシュされたコピーが残っている可能性がoperationB()
あるため、の変更がメインメモリにあるかどうかは問題ではありません。operationB()
質問#3:Q.#2での疑惑が正しい場合は、ソースコードをもう一度確認してメソッドdummyA()
のコメントを解除し、のdummyA()
呼び出しのコメントを解除しますoperationA2()
。これは他の点では悪い習慣かもしれませんが、これは違いを生むのでしょうか?私の(おそらく誤った)仮定は次のとおりです:dummyA()
threadAが(同期されたブロックのために)メインメモリからキャッシュされたデータを更新するので、によって行われたすべての変更が表示されます。つまり、今ではすべてが安全です。ちなみに、メソッド呼び出しの論理的な順序は次のとおりです。mLock
operationB()
operationA1()
operationB()
dummyA()
operationA2()
私の結論:の同期ブロックによりoperationB()
、threadBは、以前に変更された可能性のあるデータの最新の値を確認します(例:) operationA1()
。の同期ブロックによりdummyA()
、threadAは、で変更されたデータの最新のコピーを確認しoperationB()
ます。この一連の思考に誤りはありますか?