1) Das CopyOnWriteArraySet
ist eine recht einfache Implementierung - es enthält im Grunde eine Liste von Elementen in einem Array, und beim Ändern der Liste wird das Array kopiert. Iterationen und andere Zugriffe, die zu diesem Zeitpunkt ausgeführt werden, werden mit dem alten Array fortgesetzt, wodurch die Notwendigkeit einer Synchronisierung zwischen Lesern und Schreibern vermieden wird (obwohl das Schreiben selbst synchronisiert werden muss). Die normalerweise schnell eingestellten Operationen (insbesondere contains()
) sind hier ziemlich langsam, da die Arrays in linearer Zeit durchsucht werden.
Verwenden Sie dies nur für wirklich kleine Mengen, die häufig gelesen (iteriert) und selten geändert werden. (Swings Listener-Sets wären ein Beispiel, aber dies sind keine wirklichen Sets und sollten sowieso nur vom EDT verwendet werden.)
2) Collections.synchronizedSet
wickelt einfach einen synchronisierten Block um jede Methode des ursprünglichen Satzes. Sie sollten nicht direkt auf das Originalset zugreifen. Dies bedeutet, dass keine zwei Methoden des Satzes gleichzeitig ausgeführt werden können (eine wird blockiert, bis die andere beendet ist) - dies ist threadsicher, aber Sie haben keine Parallelität, wenn mehrere Threads den Satz tatsächlich verwenden. Wenn Sie den Iterator verwenden, müssen Sie normalerweise noch extern synchronisieren, um ConcurrentModificationExceptions zu vermeiden, wenn Sie den Satz zwischen Iteratoraufrufen ändern. Die Leistung entspricht der Leistung des ursprünglichen Satzes (jedoch mit etwas Synchronisationsaufwand und Blockierung bei gleichzeitiger Verwendung).
Verwenden Sie diese Option, wenn Sie nur eine geringe Parallelität haben und sicherstellen möchten, dass alle Änderungen für die anderen Threads sofort sichtbar sind.
3) ConcurrentSkipListSet
ist die gleichzeitige SortedSet
Implementierung mit den meisten grundlegenden Operationen in O (log n). Es ermöglicht das gleichzeitige Hinzufügen / Entfernen und Lesen / Iterieren, wobei die Iteration möglicherweise über Änderungen seit der Erstellung des Iterators informiert oder nicht. Die Massenoperationen sind einfach mehrere Einzelaufrufe und nicht atomar - andere Threads beobachten möglicherweise nur einige von ihnen.
Natürlich können Sie dies nur verwenden, wenn Sie eine Gesamtreihenfolge für Ihre Elemente haben. Dies scheint ein idealer Kandidat für Situationen mit hoher Parallelität zu sein, für nicht zu große Mengen (aufgrund des O (log n)).
4) Für die ConcurrentHashMap
(und die daraus abgeleitete Menge): Hier sind die meisten grundlegenden Optionen (im Durchschnitt, wenn Sie eine gute und schnelle haben hashCode()
) in O (1) (können aber zu O (n) degenerieren), wie für HashMap / HashSet. Es gibt eine begrenzte Parallelität zum Schreiben (die Tabelle ist partitioniert und der Schreibzugriff wird auf der erforderlichen Partition synchronisiert), während der Lesezugriff vollständig gleichzeitig mit sich selbst und den Schreibthreads erfolgt (die Ergebnisse der aktuellen Änderungen werden jedoch möglicherweise noch nicht angezeigt geschrieben). Der Iterator kann Änderungen seit seiner Erstellung sehen oder nicht, und Massenoperationen sind nicht atomar. Die Größenänderung ist langsam (wie bei HashMap / HashSet). Versuchen Sie daher, dies zu vermeiden, indem Sie die erforderliche Größe bei der Erstellung schätzen (und etwa 1/3 mehr davon verwenden, da die Größe geändert wird, wenn 3/4 voll ist).
Verwenden Sie diese Option, wenn Sie große Mengen und eine gute (und schnelle) Hash-Funktion haben und die Satzgröße und die erforderliche Parallelität vor dem Erstellen der Karte schätzen können.
5) Gibt es andere gleichzeitige Kartenimplementierungen, die hier verwendet werden könnten?