1) CopyOnWriteArraySet
Implementacja jest dość prosta - w zasadzie ma listę elementów w tablicy, a po zmianie listy kopiuje tablicę. Iteracje i inne dostępy, które są uruchomione w tym czasie, są kontynuowane ze starą tablicą, unikając konieczności synchronizacji między czytnikami i piszącymi (chociaż samo pisanie wymaga synchronizacji). Normalnie szybkie operacje na zbiorach (szczególnie contains()
) są tutaj dość wolne, ponieważ tablice będą przeszukiwane w czasie liniowym.
Używaj tego tylko dla naprawdę małych zestawów, które będą często czytane (iterowane) i rzadko zmieniane. (Przykładem byłyby zestawy słuchaczy swingów, ale nie są to tak naprawdę zestawy i powinny być używane tylko z EDT).
2) Collections.synchronizedSet
po prostu zawinie zsynchronizowany blok wokół każdej metody z oryginalnego zestawu. Nie powinieneś uzyskiwać bezpośredniego dostępu do oryginalnego zestawu. Oznacza to, że żadne dwie metody z zestawu nie mogą być wykonywane jednocześnie (jedna będzie blokować, dopóki druga nie zakończy się) - jest to bezpieczne wątkowo, ale nie będziesz mieć współbieżności, jeśli wiele wątków naprawdę używa zestawu. Jeśli używasz iteratora, zwykle nadal musisz zsynchronizować zewnętrzną, aby uniknąć ConcurrentModificationExceptions podczas modyfikowania zestawu między wywołaniami iteratora. Wydajność będzie podobna do oryginalnego zestawu (ale z pewnym narzutem synchronizacji i blokowaniem, jeśli jest używany jednocześnie).
Użyj tego, jeśli masz tylko niską współbieżność i chcesz mieć pewność, że wszystkie zmiany są natychmiast widoczne dla innych wątków.
3) ConcurrentSkipListSet
jest równoczesną SortedSet
implementacją, z większością podstawowych operacji w O (log n). Pozwala na jednoczesne dodawanie / usuwanie i odczytywanie / iterację, gdzie iteracja może lub nie może informować o zmianach od czasu utworzenia iteratora. Operacje zbiorcze to po prostu wielokrotne pojedyncze wywołania, a nie atomowo - inne wątki mogą obserwować tylko niektóre z nich.
Oczywiście możesz tego użyć tylko wtedy, gdy masz jakiś całkowity porządek na swoich elementach. Wygląda na to, że jest to idealny kandydat do sytuacji z dużą współbieżnością, dla niezbyt dużych zestawów (ze względu na O (log n)).
4) Dla ConcurrentHashMap
(i zbioru pochodnego): Tutaj najbardziej podstawowe opcje są (średnio, jeśli masz dobre i szybkie hashCode()
) w O (1) (ale mogą się zdegenerować do O (n)), jak dla HashMap / HashSet. Istnieje ograniczona współbieżność zapisu (tabela jest podzielona na partycje, a dostęp do zapisu zostanie zsynchronizowany na wymaganej partycji), podczas gdy dostęp do odczytu jest w pełni współbieżny z nim samym i zapisywanymi wątkami (ale może jeszcze nie widzieć wyników aktualnie wprowadzanych zmian pisemny). Iterator może, ale nie musi, widzieć zmiany od czasu jego utworzenia, a operacje zbiorcze nie są atomowe. Zmiana rozmiaru jest powolna (jak w przypadku HashMap / HashSet), dlatego staraj się tego uniknąć, szacując wymagany rozmiar podczas tworzenia (i wykorzystując o około 1/3 więcej, ponieważ zmienia rozmiar, gdy jest zapełniony w 3/4).
Użyj tego, gdy masz duże zestawy, dobrą (i szybką) funkcję skrótu i możesz oszacować rozmiar zestawu i wymaganą współbieżność przed utworzeniem mapy.
5) Czy istnieją inne współbieżne implementacje map, których można by tutaj użyć?