Я ищу тип блокировки, при котором поток, удерживающий блокировку, может передать ее другому потоку по своему выбору.
Вот почему я этого хочу:
- У меня есть класс, похожий на
ConcurrentHashMap
— специализированная коллекция, разделенная на несколько сегментов. - Большинство модификаций требуют блокировки только одного сегмента. Некоторые требуют блокировки двух сегментов (в частности, изменение ключа таким образом, чтобы он перемещался из одного сегмента в другой).
- Большинство чтений не требуют блокировки — обычно достаточно изменчивого чтения, но иногда при поиске необходимо заблокировать все сегменты сразу, если проверка количества модификаций не удалась.
- Поиск выполняется в нескольких потоках (через
ThreadPoolExecutor
) - Очень важно, чтобы функция поиска имела единообразное представление обо всех сегментах (например, она не должна пропустить запись при перемещении из одного сегмента в другой).
- Задача поиска (для одного сегмента) может быть прервана в любой момент.
Теперь я рассматриваю ситуацию, когда метод поиска был вызван в основном потоке и обнаружил, что ему необходимо заблокировать все сегменты. Все блокировки сегментов должны одновременно удерживаться основным потоком (чтобы гарантировать отсутствие помех), но не основной поток будет выполнять обновления, а один из рабочих потоков. Следовательно, я пытаюсь заставить основной поток «передавать» блокировки, как только он узнает, что у него есть согласованный снимок.
Раньше мы использовали одну блокировку для всей коллекции, но по мере ее увеличения возникало слишком много конфликтов и неприемлемо высокая задержка для небольших обновлений.
Разблокировка и повторная блокировка (на ReentrantLock
) небезопасны — другой поток может изменить сегмент до того, как рабочий поток начнет поиск.
Обычный Semaphore
может обрабатывать блокировку и разблокировку разными потоками. Тогда возникает вопрос, кто должен освободить семафор: рабочему потоку нужен способ сигнализировать о том, что он взял на себя ответственность за блокировку (поскольку он может выдать исключение до или после этого момента, а основной поток должен знать, следует ли очищать блокировку). up.) Было бы также сложно выполнить модульное тестирование, потому что вы никогда не знаете, произошло ли получение или освобождение семафора в правильном потоке.
Было бы неплохо, если бы блокировку можно было повторно использовать в других методах (где она не передается между потоками).
Я предполагаю, что требуется некоторая комбинация Semaphore
и AtomicBoolean
или AtomicReference
, но быстрый поиск в Google не выявил никаких примеров. Есть ли причина, по которой этот подход не следует использовать?