В настоящий момент я использую в своем коде ReentrantReadWriteLock для синхронизации доступа через древовидную структуру. Эта структура велика, и ее читают сразу многие потоки, время от времени модифицируя небольшие ее части, так что, похоже, она хорошо соответствует идиоме чтения-записи. Я понимаю, что с этим конкретным классом нельзя поднять блокировку чтения до блокировки записи, поэтому, согласно Javadocs, необходимо снять блокировку чтения перед получением блокировки записи. Я раньше успешно использовал этот шаблон в контекстах без повторного входа.
Однако я обнаружил, что я не могу надежно получить блокировку записи, не заблокировав навсегда. Поскольку блокировка чтения является реентерабельной, и я фактически использую ее как таковую, простой код
lock.getReadLock().unlock();
lock.getWriteLock().lock()
может заблокироваться, если я повторно получил блокировку чтения. Каждый вызов разблокировки просто уменьшает счетчик удержаний, и блокировка фактически снимается только тогда, когда счетчик удержаний достигает нуля.
ИЗМЕНИТЬ, чтобы прояснить это, поскольку я не думаю, что изначально слишком хорошо объяснил это - я знаю, что в этом классе нет встроенной эскалации блокировки, и что мне нужно просто отпустить прочитанное lock и получить блокировку записи. Моя проблема заключается в том, что независимо от того, что делают другие потоки, вызов getReadLock().unlock()
может фактически не освободить блокировку этого потока, если он получил ее повторно, и в этом случае вызов getWriteLock().lock()
будет заблокирован навсегда, поскольку этот поток все еще удерживает блокировку чтения и, таким образом, сам себя блокирует.
Например, этот фрагмент кода никогда не достигнет оператора println, даже если он выполняется в однопоточном режиме без доступа других потоков к блокировке:
final ReadWriteLock lock = new ReentrantReadWriteLock();
lock.getReadLock().lock();
// In real code we would go call other methods that end up calling back and
// thus locking again
lock.getReadLock().lock();
// Now we do some stuff and realise we need to write so try to escalate the
// lock as per the Javadocs and the above description
lock.getReadLock().unlock(); // Does not actually release the lock
lock.getWriteLock().lock(); // Blocks as some thread (this one!) holds read lock
System.out.println("Will never get here");
Поэтому я спрашиваю, есть ли хорошая идиома, чтобы справиться с этой ситуацией? В частности, когда поток, который удерживает блокировку чтения (возможно, повторно входим), обнаруживает, что ему нужно выполнить некоторую запись, и, таким образом, хочет «приостановить» свою собственную блокировку чтения, чтобы снять блокировку записи (блокировка по мере необходимости в других потоках для освободить блокировку чтения), а затем "вернуть" блокировку чтения в том же состоянии?
Поскольку эта реализация ReadWriteLock была специально разработана для повторной входимости, конечно, есть какой-нибудь разумный способ поднять блокировку чтения до блокировки записи, когда блокировки могут быть получены повторно? Это критическая часть, которая означает, что наивный подход не работает.