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) Существуют ли другие параллельные реализации карты, которые можно использовать здесь?