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