Anleitung zu CopyOnWriteArrayList

1. Übersicht

In diesem kurzen Artikel sehen wir uns die CopyOnWriteArrayList aus dem Paket java.util.concurrent an.

Dies ist ein sehr nützliches Konstrukt in Multithread-Programmen - wenn wir eine Liste threadsicher ohne explizite Synchronisierung durchlaufen möchten.

2. CopyOnWriteArrayList- API

Das Design der CopyOnWriteArrayList verwendet eine interessante Technik, um sie threadsicher zu machen, ohne dass eine Synchronisierung erforderlich ist. Wenn wir eine der Änderungsmethoden verwenden, z. B. add () oder remove (), wird der gesamte Inhalt der CopyOnWriteArrayList in die neue interne Kopie kopiert.

Aufgrund dieser einfachen Tatsache können wir die Liste auf sichere Weise durchlaufen, selbst wenn gleichzeitig Änderungen vorgenommen werden .

Wenn wir die iterator () -Methode in der CopyOnWriteArrayList aufrufen, erhalten wir einen Iterator zurück, der durch den unveränderlichen Snapshot des Inhalts der CopyOnWriteArrayList gesichert ist .

Der Inhalt ist eine exakte Kopie der Daten, die sich in einer ArrayList ab dem Zeitpunkt der Erstellung des Iterators befinden . Selbst wenn in der Zwischenzeit ein anderer Thread ein Element zur Liste hinzufügt oder daraus entfernt, erstellt diese Änderung eine neue Kopie der Daten, die für die weitere Suche nach Daten aus dieser Liste verwendet werden.

Die Eigenschaften dieser Datenstruktur machen sie besonders nützlich, wenn wir sie häufiger durchlaufen als ändern. Wenn das Hinzufügen von Elementen in unserem Szenario häufig vorkommt, ist CopyOnWriteArrayList keine gute Wahl, da die zusätzlichen Kopien definitiv zu einer unterdurchschnittlichen Leistung führen.

3. Durch Iterieren über CopyOnWriteArrayList beim Einfügen

Angenommen , wir erstellen eine Instanz der CopyOnWriteArrayList , in der Ganzzahlen gespeichert sind :

CopyOnWriteArrayList numbers = new CopyOnWriteArrayList(new Integer[]{1, 3, 5, 8});

Als Nächstes möchten wir dieses Array durchlaufen, also erstellen wir eine Iterator- Instanz:

Iterator iterator = numbers.iterator();

Nachdem der Iterator erstellt wird, fügen wir ein neues Element in die Zahlen Liste:

numbers.add(10);

Beachten Sie, dass wir beim Erstellen eines Iterators für die CopyOnWriteArrayList eine unveränderliche Momentaufnahme der Daten in der Liste zum Zeitpunkt des Aufrufs von iterator () erhalten .

Aus diesem Grund wird beim Durchlaufen nicht die Nummer 10 in der Iteration angezeigt:

List result = new LinkedList(); iterator.forEachRemaining(result::add); assertThat(result).containsOnly(1, 3, 5, 8);

Die nachfolgende Iteration mit dem neu erstellten Iterator gibt auch die hinzugefügte Nummer 10 zurück:

Iterator iterator2 = numbers.iterator(); List result2 = new LinkedList(); iterator2.forEachRemaining(result2::add); assertThat(result2).containsOnly(1, 3, 5, 8, 10);

4. Das Entfernen während des Iterierens ist nicht zulässig

Die CopyOnWriteArrayList wurde erstellt, um die Möglichkeit zu bieten, sicher über Elemente zu iterieren, selbst wenn die zugrunde liegende Liste geändert wird.

Aufgrund des Kopiermechanismus ist die Operation remove () für den zurückgegebenen Iterator nicht zulässig. Dies führt zu UnsupportedOperationException:

@Test(expected = UnsupportedOperationException.class) public void whenIterateOverItAndTryToRemoveElement_thenShouldThrowException() { CopyOnWriteArrayList numbers = new CopyOnWriteArrayList(new Integer[]{1, 3, 5, 8}); Iterator iterator = numbers.iterator(); while (iterator.hasNext()) { iterator.remove(); } }

5. Schlussfolgerung

In diesem kurzen Tutorial haben wir uns die Implementierung von CopyOnWriteArrayList aus dem Paket java.util.concurrent angesehen .

Wir haben die interessante Semantik dieser Liste gesehen und gesehen, wie sie threadsicher iteriert werden kann, während andere Threads weiterhin Elemente einfügen oder daraus entfernen können.

Die Implementierung all dieser Beispiele und Codefragmente finden Sie im GitHub-Projekt - dies ist ein Maven-Projekt, daher sollte es einfach zu importieren und auszuführen sein, wie es ist.