Блокировка, которую можно перенести из одного потока в другой - PullRequest
3 голосов
/ 27 января 2011

Я ищу тип блокировки, где поток, который удерживает блокировку, может передать ее другому потоку по своему выбору.

Вот почему я хочу это:

  • У меня есть класс, похожий на ConcurrentHashMap - специализированная коллекция, которая разделена на несколько сегментов
  • Для большинства модификаций требуется блокировка только одного сегмента. Для некоторых требуется блокировка двух сегментов (в частности, изменение ключа для его перемещения из одного сегмента в другой).
  • Для большинства операций чтения не требуется блокировка - обычно достаточно нестабильного чтения, но иногда при поиске необходимо заблокировать все сегменты одновременно, если проверка количества изменений не удалась
  • Поиск осуществляется в несколько потоков (через ThreadPoolExecutor)
  • Важно, чтобы функция поиска имела согласованное представление всех сегментов (например, она не должна пропускать запись при перемещении из одного сегмента в другой).
  • Задача поиска (для одного сегмента) может быть прервана в любое время.

Теперь я рассматриваю ситуацию, когда метод поиска был вызван в главном потоке и обнаруживает, что он должен заблокировать все сегменты. Все блокировки сегментов должны одновременно удерживаться основным потоком (чтобы гарантировать отсутствие помех), но это не основной поток, который будет выполнять обновления - это один из рабочих потоков. Поэтому я пытаюсь заставить основной поток «передавать» блокировки, как только узнает, что у него непротиворечивый снимок.

Раньше мы использовали одну блокировку для всей коллекции, но по мере ее увеличения возникало слишком много конфликтов и недопустимо высокая задержка для небольших обновлений.

Разблокировка и повторная блокировка (на ReentrantLock) небезопасна - другой поток может изменить сегмент до того, как рабочий поток начнет поиск.

Простой Semaphore может обрабатывать блокировку и разблокировку различными потоками. Тогда возникает вопрос, кто должен освобождать семафор - рабочему потоку нужен способ сообщить о том, что он приобрел блокировку (поскольку он может вызвать исключение до или после этой точки, а основной поток должен знать, следует ли очищать вверх.) Также было бы сложно провести юнит-тестирование, потому что вы никогда не узнаете, произошло ли получение или освобождение семафора в правильном потоке.

Было бы бонусом, если бы блокировка могла использоваться повторно в других методах (где она не передается между потоками).

Я предполагаю, что требуется некоторая комбинация Semaphore и AtomicBoolean или AtomicReference, но быстрый поиск в Google не выявил никаких примеров. Есть ли причина, по которой этот подход не следует использовать?

1 Ответ

2 голосов
/ 27 января 2011

Может работать обратное использование блокировки чтения / записи. В этой идиоме держатели блокировки чтения выполняют параллельные записи в структуру данных (например, независимые слоты в массиве). Блокировка записи используется для получения монопольного доступа для выполнения согласованного чтения (например, суммирование массива). Это редко используемая идиома, но элегантная в тех странных случаях. Ваша проблема немного сложна, но это может, по крайней мере, вдохновить на конкретное решение. Я подозреваю, что при более глубоком понимании проблему можно упростить, чтобы более подходило классическое решение.

...