9

「Java Concurrency in Practice」のセクション 14.6.1 で ReentrantLock の実装の詳細について読んだのですが、注釈の何かが私を混乱させました。

保護された状態操作メソッドには揮発性の読み取りまたは書き込みのメモリ セマンティクスがあり、ReentrantLock はgetStateを呼び出した後にのみ所有者フィールドを読み取り、setState を呼び出す前にのみそれを書き込むように注意するため、ReentrantLock は同期状態のメモリ セマンティクスに便乗できます。セクション16.1.4を参照してください。

それが参照するコード:

protected boolean tryAcquire(int ignored) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c ==0) {
        if (compareAndSetState(0, 1)) {
             owner = current;
             return true;
        }
     } else if (current == owner) {
         setState(c+1);
         return true;
     }
     return false;
}

そして、これは の の簡略化されnonfairTryAcquireたコードだと思いますReentrantLock.Sync

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

したがって、不可解な部分は、 のowner単なるインスタンス変数である の設定が、他のスレッドでどのAbstractOwnableSynchronizerように見えるようになるかです。else if (current == owner)実際、 の読み込みはownerの呼び出しの後でありgetState()(そしてstateは のvolatile修飾変数ですAQS)、 の設定後はowner何もありません (同期セマンティクスを課すことができます)。データ競合が発生?

さて、この本の権威と徹底的にテストされたコードに照らして、2 つの可能性が頭に浮かびます。

  1. owner = current設定が隠された作業を行う前の完全なバリア (mfence または 'lock'ed 命令) 。しかし、私がいくつかの有名な記事から学んだことによると、フル バリアは、その後の読み取りだけでなく、その前の書き込みにも関心があります。そうですね、この可能性が正しいとすれば、「JCIP」のいくつかの文は不適切に述べられている可能性があります。

  2. if-else の別のブランチにありますが、「地理的に」はコード スニペットのsetState(c+1)後に来ることに気付きました。owner = currentコメントが言っていることが真実である場合、挿入されたバリアが別のブランチでsetSate(c+1)同期セマンティクスを課すことができるということですか?owner = current

私はこの分野の初心者であり、いくつかの優れたブログが JVM の基礎となるものを理解するのに大いに役立ちます (順序なし)。

常に素晴らしい: http://g.oswego.edu/dl/jmm/cookbook.html

宿題をしてインターネットを検索した後、満足のいく結論に達しません。

これが長すぎたり不明確だったりする場合はご容赦ください (英語は私の母国語ではありません)。これで私を助けてください、関連するものは何でも感謝しています。

4

2 に答える 2

3

owner = current;(CAS の後) とif (current == owner)(状態を読み取って >0 かどうかを確認した後) の間で競合が発生する可能性があると思われます。

このコードを切り離して考えると、あなたの推論は正しいと思います。ただし、次のことも考慮する必要がありtryReleaseます。

 123:         protected final boolean tryRelease(int releases) {
 124:             int c = getState() - releases;
 125:             if (Thread.currentThread() != getExclusiveOwnerThread())
 126:                 throw new IllegalMonitorStateException();
 127:             boolean free = false;
 128:             if (c == 0) {
 129:                 free = true;
 130:                 setExclusiveOwnerThread(null);
 131:             }
 132:             setState(c);
 133:             return free;
 134:         }

ここではnull、状態が 0 に設定される前に所有者が に設定されます。最初にロックを取得するには、状態が 0 でなければならないため、所有者はnullです。

その結果、

  • スレッドが に達しif (current == owner)た場合c=1
    • 所有スレッドである可能性があります。その場合、所有者は正しく、状態はインクリメントされます。
    • 新しい所有者を見ることができるかどうかに関係なく、別のスレッドにすることができます。
      • それが見えれば万事OKです。
      • そうでない場合は、 が表示さnullれますが、これも問題ありません。
  • スレッドが に達しif (current == owner)た場合c>1
    • 所有スレッドである可能性があります。その場合、所有者は正しく、状態はインクリメントされます。
    • 別のスレッドである可能性がありますが、所有者は確かに正しいでしょう。

JCIP の「getState を呼び出した後にのみ所有者フィールドを読み取り、setState を呼び出す前にのみ書き込む」という脚注が誤解を招くことに同意します。ownerを呼び出す前setStateにを書き込みますが、 は書き込みtryReleaseませんtryAcquire

于 2013-09-12T07:25:21.013 に答える