У меня есть несколько тесно связанных вопросов, которые возникли при попытке использовать 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;
}
}