I have a few closely related questions that popped up while trying to use ReentrantReadWriteLock
to control access to a fairly complex data structure that has a number of different read and write operations. As per examples in the documentation, I've fetched a read and a write lock, and I'm using them to (from what I can tell) successfully manage concurrent access to this data structure. However, while debugging another issue, I noticed that sometimes I acquire more than one read lock in the same thread. The basic reason for this is that I have a number of complex queries that invoke simpler ones (see the example below). The complex query can be thought of as a transaction, i.e., there should be no writes between the getVersion()
and the access to data
in myQuery
. This current solution works, but it means that in some places in the code, I will have multiple read locks possessed by the same thread. I've noticed that the write equivalent has an isHeldByCurrentThread()
method, but this is oddly absent in readLock
. I couldn't find out the following:
- is it bad (poor style, performance or risk of future bugs) to have multiple read locks in the same thread?
- is is bad to use
WriteLock.isHeldByCurrentThread
to check for errors (e.g., expecting a write lock but none is present, or as a condition to release the write lock)? - is there a best practice to decide how to proceed if this is an issue? Do I pass a
lockedAquired
boolean to methods that may be called from code that already possesses a lock, do I ensure they are uniquely acquired, or should I useReadLock.tryLock()
?
Here's the code sample:
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;
}
}