Я сейчас использую в своем коде ReentrantReadWriteLock для синхронизации доступа по древовидной структуре. Эта структура является большой и может быть прочитана многими потоками одновременно с редкими изменениями ее небольших частей, поэтому она, похоже, хорошо подходит для идиомы чтения-записи. Я понимаю, что с этим конкретным классом нельзя повысить блокировку чтения до блокировки записи, поэтому согласно Javadocs необходимо снять блокировку чтения до получения блокировки записи. Ранее я успешно использовал этот шаблон в нерецензируемых контекстах.
Однако я обнаружил, что не могу надежно получить блокировку записи без блокирования навсегда. Поскольку блокировка чтения является реентерабельной, и я фактически использую ее как таковую, простой код
lock.getReadLock().unlock();
lock.getWriteLock().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 была специально разработана для повторного входа, наверняка есть какой-то разумный способ повысить блокировку чтения до блокировки записи, когда блокировки могут быть получены повторно? Это критическая часть, которая означает, что наивный подход не работает.