8

Java メモリ モデルではsynchronize、同じモニター上で同期するブロックが、それらのブロック内で変更された変数に対してビフォア アフター リレーションを強制することが義務付けられています。例:

// in thread A
synchronized( lock )
{
  x = true;
}

// in thread B
synchronized( lock )
{
  System.out.println( x );
}

この場合x==true、スレッド A がそのブロックをすでに通過している限り、スレッド B が参照することが保証されますsynchronized。現在、より柔軟な (そして高速であると言われている) ロックjava.util.concurrent、特にReentrantReadWriteLock. したがって、例は次のようになります。

編集:マット bで指摘されているように、コードを誤って変換したため、例が壊れていました。次のように修正されました。

// in thread A
lock.writeLock().lock();
{
  x = true;
}
lock.writeLock().unlock();

// in thread B
lock.readLock().lock();
{
  System.out.println( x );
}
lock.readLock().unlock();

しかし、メモリー・モデルの仕様の中で、そのようなロックが必要な順序付けを暗示しているというヒントは見たことがありません。実装を調べると、内部の volatile 変数へのアクセスに依存しているようですAbstractQueuedSynchronizer(少なくとも太陽の実装では)。ただし、これは仕様の一部ではなく、さらに、不揮発性変数へのアクセスは、これらの変数によって与えられるメモリバリアによってカバーされるとは実際には考えられていませんよね?

だから、ここに私の質問があります:

  • synchronized「古い」ブロックと同じ順序を想定しても安全ですか?
  • これはどこかに文書化されていますか?
  • volatile 変数にアクセスすると、他の変数のメモリ バリアになりますか?

よろしく、ステフェン

--

やなもんさんへのコメント:

次のコードを見てください。

// in thread a
x = 1;
synchronized ( a ) { y = 2; }
z = 3;

// in thread b
System.out.println( x );
synchronized ( a ) { System.out.println( y ); }
System.out.println( z );

私が理解したことから、メモリバリアは2番目の出力に2を表示するように強制しますが、他の変数への影響は保証されていません...? では、これを volatile 変数へのアクセスとどのように比較できますか?

4

4 に答える 4

5

API-docから:

すべての Lock 実装は、Java 言語仕様、第 3 版 (17.4 メモリ モデル) で説明されているように、組み込みのモニター ロックによって提供されるものと同じメモリ同期セマンティクスを適用する必要があります。

* A successful lock operation has the same memory synchronization effects as a successful Lock action.
* A successful unlock operation has the same memory synchronization effects as a successful Unlock action.

失敗したロック操作とロック解除操作、および再入可能なロック操作とロック解除操作では、メモリ同期効果は必要ありません。

于 2010-04-05T00:43:53.337 に答える
4

メモリ モデルのセマンティクスが何を保証するかという問題を超えて、投稿しているコードにはいくつかの問題があると思います。

  1. 同じロックで2 回同期しています - これは不要です。実装を使用する場合、ブロックLockを使用する必要はありません。synchronized
  2. a を使用する標準的なイディオムLockは、try-finally ブロックで使用して、誤ってロックが解除されるのを防ぐことです (ブロックのように、現在のブロックに入るときにロックが自動的に解除されないためsynchronized)。

Lock次のようなものでa を使用する必要があります。

lock.lock();
try {
    //do stuff
}
finally { 
    lock.unlock();
}
于 2010-04-05T01:10:09.483 に答える
1

volatile 変数の読み取りと書き込みは、操作の順序付けの前後に発生するようになりました。volatile 変数への書き込みはモニターの解放と同じ効果があり、変数の読み取りはモニターの取得と同じ効果があります。次の例では、もう少し明確になります。

volatile boolean memoryBarrier = false;
int unguardedValue = 0;

//thread a:
unguardedValue = 10;
memoryBarrier = true;

// thread b
if (memoryBarrier) {
  // unguardedValue is guaranteed to be read as 10;
}

ReentrantLockしかし、あなたが提供したサンプルコードは、使用するように設計されているため、実際に使用しているようには見えませんでした。

  1. Locka の使用をJava の組み込みキーワードで囲むと、syncronized効果的にロックへのアクセスが既にシングル スレッド化されているため、Lock実際の作業を行う機会がなくなります。
  2. リリースの取得は、Lock以下のパターンに従って行う必要があります。これは、の Java ドキュメントで概説されています。Lock

lock.readLock().lock();
try {
  // Do work
} finally {
  lock.readLock.unlock();
}

于 2010-04-05T01:11:53.037 に答える
1

やなもん、あなたが正しいかどうかはわかりませんが、あなたがしている議論とは異なる理由があります.

unguardedVariable 変数は、 memoryBarrier が true に設定されたにその値が 10 に設定されるように、スレッド "a" で並べ替えられる場合があります。

「そのスレッド内で並べ替えが検出されない限り、1 つのスレッド内の操作がプログラムによって指定された順序で実行されるという保証はありません。たとえ並べ替えが他のスレッドに明らかであっても

Java Concurrency in Practise、Brian Goetz、p34

更新: 私が言ったことは、古いメモリ モデルの場合に当てはまります。したがって、一度だけ実行できる場所が必要な場合は、私の主張が成り立ちます。ただし、新しいメモリ モデルでは、揮発性アクセスが存在する場合の不揮発性変数の並べ替えに関するセマンティクスがより厳密になっているため、そうではありません ( http://www.cs.umd.edu/~pughを参照)。 /java/memoryModel/jsr-133-faq.html#volatile )。

于 2010-10-30T13:02:02.297 に答える