19

ロックを取得したスレッドとは異なるスレッドによってロックが解放される可能性があるため、再入可能ではないReadWriteLockが必要です。(これは、IllegalMonitorStateExceptionが断続的に発生し始めたときにわかりました。)

非再入可能が正しい用語であるかどうかはわかりません。ReentrantLockを使用すると、現在保持しているスレッドをロックして、再度取得できます。私はこの振る舞いを望まないので、それを「非再入可能」と呼んでいます。

コンテキストは、スレッドプールを使用するソケットサーバーがあるということです。接続ごとにスレッドはありません。リクエストは異なるスレッドによって処理される場合があります。クライアント接続では、あるリクエストをロックインし、別のリクエストをロック解除する必要がある場合があります。リクエストは異なるスレッドで処理される可能性があるため、異なるスレッドでロックおよびロック解除できる必要があります。

この質問のために、私はこの構成を維持する必要があり、実際にはさまざまな要求、したがっておそらくさまざまなスレッドでロックおよびロック解除する必要があると想定します。

複数の「リーダー」または排他的な「ライター」を許可する必要があるため、これはReadWriteLockです。

これはAbstractQueuedSynchronizerを使用して記述できるようですが、自分で記述した場合、微妙な間違いを犯してしまうのではないかと心配しています。AbstractQueuedSynchronizerを使用したさまざまな例を見つけることができますが、ReadWriteLockは見つかりません。

OpenJDK ReentrantReadWriteLockソースを取得して、再入可能部分を削除しようとすることもできますが、正しく理解できないのではないかと心配しています。

GuavaとApacheCommonsを調べましたが、適切なものは見つかりませんでした。Apache CommonsにはRWLockManagerがあり、必要なことを実行できる可能性がありますが、よくわかりません。必要以上に複雑に見えます。

4

4 に答える 4

35

セマフォは、異なるスレッドが許可の取得と解放を実行できるようにします。スレッドは、すべてのパーミットが解放され、他のスレッドが追加のパーミットを取得できなくなるまで待機するため、排他的書き込みは、すべてのパーミットを持つことと同じです。

final int PERMITS = Integer.MAX_VALUE;
Semaphore semaphore = new Semaphore(PERMITS);

// read
semaphore.acquire(1);
try { ... }
finally {
  semaphore.release(1);
}

// write
semaphore.acquire(PERMITS);
try { ... }
finally {
  semaphore.release(PERMITS);
}
于 2012-05-23T01:33:44.807 に答える
2

すでに別の回答を受け入れていることは知っています。しかし、あなたは自分自身にかなりの悪夢をもたらすと思います。最終的に、クライアントは戻ってきてそれらの許可を解放することに失敗し、「ライター」が書き込みを行わない理由を疑問に思うようになります。

もし私がそれをしていたら、私は次のようにします:

Client issues a request to start a transaction
The initial request creates a task (Runnable/Callable) and places it in an Executor for execution
The initial request also registers that task in a Map by transaction id

Client issues the second request to close the transaction
The close request finds the task by transaction id in a map
The close request calls a method on the task to indicate that it should close (probably a signal on a Condition or if data needs to be passed, placing an object in a BlockingQueue)

これで、トランザクション タスクのコードは次のようになります。

public void run() {
    readWriteLock.readLock().lock();
    try {
        //do stuff for initializing this transaction
        if (condition.await(someDurationAsLong, someTimeUnit)( {
            //do the rest of the transaction stuff
        } else {
            //do some other stuff to back out the transaction
        }
    } finally {
        readWriteLock.readLock.unlock();
    }
}
于 2012-05-23T21:14:49.423 に答える
1

特に、何が必要か完全にはわかりません。なぜそれが読み取り/書き込みロックである必要があるのか​​、しかし多くのスレッドで処理する必要のあるタスクがあり、それを同時に処理/アクセスしたくない場合は、実際にはConcurrentMapなどを使用します。

タスクをマップから削除するか、特別な「ロックオブジェクト」に置き換えて、ロックされていることを示すことができます。更新された状態のタスクをマップに戻して別のスレッドに引き継がせるか、あるいはタスクを次のスレッドに直接渡して、代わりにタスクをマップに戻すことができます。

于 2012-05-22T23:53:33.240 に答える
1

彼らは、com.sun.corba.se.impl.orbutil.concurrent.Mutex; を非推奨にすることで、これにボールを落としたようです。

つまり、再入不可のロックは必要ないと考える人が正気であるということです。ここで、リエントラントの定義について議論して時間を無駄にしています (フレームワークごとに意味がわずかに変わる可能性があります)。はい、同じスレッドでロックを試みたいのですが、それはそんなに悪いことですか? 他に病気があるため、デッドロックすることはありません。同じスレッドでロックする再入不可のロックは、ユーザーが同じボタンをすばやく繰り返し押す GUI アプリでのエラーを防ぐのに非常に役立ちます。そこに行って、それをして、QTは正しかった...再び。

于 2015-05-08T20:55:06.623 に答える