Java: использование ConcurrentHashMap в качестве менеджера блокировки
Я пишу очень параллельное приложение, которому нужен доступ к большому детализированному набору общих ресурсов. В настоящее время я пишу глобальный менеджер блокировки, чтобы организовать это. Мне интересно, могу ли я воспользоваться контрабандой ConcurrentHashMap
и использовать это для обработки блокировки? Я имею в виду такую систему:
- Единый глобальный
ConcurrentHashMap
объект содержит отображение между уникальным строковым идентификатором ресурса изаблокировать защиту этого ресурсауникальный идентификатор потока, использующего ресурс - Настройте фактор параллелизма, чтобы отразить потребность в высоком уровне параллелизма
- Замки приобретаются с использованием атомных условных
replace(K key, V oldValue, V newValue)
метод в хэш-карте - Чтобы предотвратить конфликт блокировки при блокировке нескольких ресурсов, блокировки должны быть получены в алфавитном порядке
Есть ли серьезные проблемы с настройкой? Как будет выступление?
Я знаю, что это, вероятно, будет намного медленнее и более трудоемким, чем правильно написанная система блокировки, но я бы предпочел не тратить дни, пытаясь написать свою собственную, особенно учитывая, что я, вероятно, не смогу соответствовать профессионально Java написанный параллельный код, реализующий карту.
Кроме того, я никогда не использовал ConcurrentHashMap в условиях высокой нагрузки, поэтому мне интересно следующее:
- Насколько хорошо это будет масштабироваться до большого количества элементов? (Я смотрю на ~1 000 000, являющуюся хорошим пределом. Если я выйду за пределы этого, я буду готов переписать это более эффективно)
- В документации говорится, что изменение размеров происходит "относительно" медленно. Насколько это медленно? Возможно, мне придется менять размер карты раз в минуту или около того. Это будет проблематично с размером карты, на которую я смотрю?
Редактировать: Спасибо, Хольгер, за то, что указал, что у HashMaps не должно быть такой большой проблемы с масштабированием
Кроме того, есть ли лучший / более стандартный метод там? Я не могу найти места, где используется такая система, поэтому я предполагаю, что либо я не вижу серьезного недостатка, либо есть что-то еще.
Редактировать:
Приложение, которое я пишу, является сетевым сервисом, обрабатывающим различное количество запросов. Я использую проект Grizzly, чтобы сбалансировать запросы между несколькими потоками. Каждый запрос использует небольшое количество общих ресурсов (~30), поэтому, как правило, я не ожидаю большого количества споров. Запросы обычно заканчивают работу с ресурсами менее чем за 500 мс. Таким образом, я бы согласился с небольшим количеством блокировок / непрерывных опросов, так как запросы не очень чувствительны ко времени и количество конфликтов должно быть минимальным.
В общем, видя, что правильное решение будет очень похоже на то, как ConcurrentHashMap
работает за кулисами, мне интересно, смогу ли я безопасно использовать это в качестве ярлыка вместо написания / отладки / тестирования моей собственной версии.
1 ответ
Проблема изменения размера не актуальна, так как вы уже указали оценку количества элементов в вашем вопросе. Так что вы можете дать ConcurrentHashMap
начальная емкость достаточно большая, чтобы избежать перефразирования.
Производительность будет зависеть не от количества элементов, это главная цель хэширования, а от количества одновременных потоков.
Основная проблема в том, что у вас нет плана того, как обрабатывать неудачные блокировки. Если вы не хотите опрашивать, пока блокировка не будет успешной (что не рекомендуется), вам нужен способ перевести поток в спящий режим, который подразумевает, что поток, в настоящее время владеющий блокировкой, должен пробуждать спящий поток при освобождении, если он существует. Таким образом, вы в конечном итоге требует обычного Lock
имеет ConcurrentHashMap
не предлагает.
Создание Lock
за элемент (как вы сказали ~1000000) не будет решением.
Решение будет выглядеть как ConcurrentHashMap
работает внутри. Учитывая определенный уровень параллелизма, то есть количество потоков, которые у вас могут быть (округлены), вы создаете это число Lock
s (это было бы гораздо меньше, чем 1 000 000).
Теперь вы назначаете каждому элементу один из Lock
s. Простое назначение будет основано на элементе hashCode
при условии, что оно стабильно. Затем блокировка элемента означает блокировку назначенного Lock
что дает вам до настроенного уровня параллелизма, если все в настоящее время заблокированные элементы отображаются на разные Lock
s.
Это может означать, что потоки, блокирующие разные элементы, блокируют друг друга, если элементы отображаются на одно и то же. Lock
, но с предсказуемой вероятностью. Вы можете попытаться настроить уровень параллелизма (как указано, использовать число, превышающее число потоков), чтобы найти лучший компромисс.
Большим преимуществом этого подхода является то, что вам не нужно поддерживать структуру данных, которая зависит от количества элементов. Афаик, новая параллель ClassLoader
использует похожую технику.