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