ReentrantReadWriteLock
多数の異なる読み取りおよび書き込み操作を持つかなり複雑なデータ構造へのアクセスを制御しようとしているときに、いくつかの密接に関連する質問がポップアップしました。ドキュメントの例に従って、読み取りロックと書き込みロックをフェッチし、それらを使用して (私が知る限り) このデータ構造への同時アクセスを正常に管理しています。ただし、別の問題をデバッグしているときに、同じスレッドで複数の読み取りロックを取得する場合があることに気付きました。これの基本的な理由は、単純なクエリを呼び出す複雑なクエリが多数あるためです (以下の例を参照)。複雑なクエリはトランザクションと考えることができます。つまり、 と へのgetVersion()
アクセスの間に書き込みがあってはなりませんdata
myQuery
. この現在の解決策は機能しますが、コード内のいくつかの場所で、同じスレッドが複数の読み取りロックを所有することになります。同等の書き込みにはisHeldByCurrentThread()
メソッドがあることに気付きましたが、これは奇妙なことに にはありませんreadLock
。以下のことがわかりませんでした。
- 同じスレッドに複数の読み取りロックを設定するのは悪いことですか (スタイル、パフォーマンス、または将来のバグのリスク)?
- エラーをチェックするために使用
WriteLock.isHeldByCurrentThread
するのは不適切ですか (たとえば、書き込みロックを期待しているが何も存在しない、または書き込みロックを解放する条件として)? - これが問題である場合、どのように進めるかを決定するためのベストプラクティスはありますか?
lockedAquired
既にロックを所有しているコードから呼び出される可能性のあるメソッドにブール値を渡すか、それらが一意に取得されるようにするか、または使用する必要がありますReadLock.tryLock()
か?
コードサンプルは次のとおりです。
public class GraphWorldModel {
//unfair RW Lock (default, see javadoc)
protected final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(false);
protected final ReentrantReadWriteLock.ReadLock readLock = rwl.readLock();
protected final ReentrantReadWriteLock.WriteLock writeLock = rwl.writeLock();
protected long versionID = 0;
protected Object data = null;
@Override
public long getVersion() {
long result = -1;
readLock.lock();
try {
result = this.versionID;
} finally {
readLock.unlock();
}
return result;
}
public ResultObject myQuery() {
//do some work
readLock.lock();
try {
long version = getVersion();
ResultObject result = new ResultObject(this.data, version);
//note: querying version and result should be atomic!
} finally {
readLock.unlock();
}
//do some more work
return result;
}
}