1) Это CopyOnWriteArraySet
довольно простая реализация - она в основном имеет список элементов в массиве и при изменении списка копирует массив. Итерации и другие обращения, которые выполняются в это время, продолжаются со старым массивом, избегая необходимости синхронизации между читателями и пишущими (хотя сама запись должна быть синхронизирована). Обычно операции быстрого набора (особенно contains()
) довольно медленные, так как массивы будут искать в линейном времени.
Используйте это только для действительно небольших наборов, которые будут часто читаться (повторяться) и изменяться редко. (Наборы слушателей Swings были бы примером, но на самом деле это не наборы, и в любом случае их следует использовать только из EDT.)
2) Collections.synchronizedSet
просто обернет синхронизированный блок вокруг каждого метода исходного набора. Вы не должны получать доступ к оригинальному набору напрямую. Это означает, что никакие два метода набора не могут выполняться одновременно (один будет блокироваться, пока другой не завершится) - это потокобезопасно, но у вас не будет параллелизма, если множество потоков действительно использует набор. Если вы используете итератор, вам, как правило, по-прежнему необходимо выполнять внешнюю синхронизацию, чтобы избежать исключений ConcurrentModificationExceptions при изменении набора между вызовами итератора. Производительность будет аналогична производительности исходного набора (но с некоторыми накладными расходами синхронизации и блокировкой, если используется одновременно).
Используйте это, если у вас только низкий уровень параллелизма и вы хотите, чтобы все изменения были немедленно видны другим потокам.
3) ConcurrentSkipListSet
является параллельной SortedSet
реализацией, с большинством основных операций в O (log n). Это позволяет одновременное добавление / удаление и чтение / итерацию, когда итерация может или не может рассказать об изменениях с момента создания итератора. Массовые операции - это просто множественные одиночные вызовы, а не атомарно - другие потоки могут наблюдать только некоторые из них.
Очевидно, что вы можете использовать это, только если у вас есть общий порядок ваших элементов. Это выглядит как идеальный кандидат для ситуаций с высоким параллелизмом, для не слишком больших множеств (из-за O (log n)).
4) Для ConcurrentHashMap
(и набора, полученного из него): здесь большинство основных опций (в среднем, если у вас хорошо и быстро hashCode()
) в O (1) (но может выродиться в O (n)), как для HashMap / HashSet. Существует ограниченный параллелизм для записи (таблица разбита на разделы, и доступ на запись будет синхронизирован на нужном разделе), в то время как доступ на чтение полностью параллелен для самого себя и потоков записи (но может еще не увидеть результаты изменений, которые в данный момент находятся написано). Итератор может видеть или не видеть изменения, так как он был создан, и массовые операции не являются атомарными. Изменение размера происходит медленно (как для HashMap / HashSet), поэтому постарайтесь избежать этого, оценивая необходимый размер при создании (и используя примерно 1/3 от этого, поскольку он изменяет размер при заполнении 3/4).
Используйте это, когда у вас большие наборы, хорошая (и быстрая) хеш-функция, и вы можете оценить размер набора и необходимый параллелизм перед созданием карты.
5) Есть ли другие параллельные реализации карт, которые можно использовать здесь?