Anleitung zur CopyOnWriteArrayList

Anleitung zu CopyOnWriteArrayList

1. Überblick

In diesem kurzen Artikel werden dieCopyOnWriteArrayList aus demjava.util.concurrent-Paket betrachtet.

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

2. CopyOnWriteArrayList API

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

Aufgrund dieser einfachen Tatsache istwe can iterate over the list in a safe way, even when concurrent modification is happening.

Wenn wir dieiterator()-Methode fürCopyOnWriteArrayList, aufrufen, erhalten wir einIterator zurück, das durch den unveränderlichen Snapshot des Inhalts derCopyOnWriteArrayList gesichert ist.

Der Inhalt ist eine exakte Kopie der Daten, die sich innerhalb einesArrayList ab dem Zeitpunkt der Erstellung desIterator 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, istCopyOnWriteArrayListkeine gute Wahl, da die zusätzlichen Kopien definitiv zu einer unterdurchschnittlichen Leistung führen.

3. Iterieren überCopyOnWriteArrayList beim Einfügen

Nehmen wir an, wir erstellen eine Instanz vonCopyOnWriteArrayList, in der Ganzzahlen gespeichert sind:

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

Als nächstes wollen wir über dieses Array iterieren, also erstellen wir eineIterator-Instanz:

Iterator iterator = numbers.iterator();

Nachdem dieIterator erstellt wurden, fügen wir der Liste dernumbers ein neues Element hinzu:

numbers.add(10);

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

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

List result = new LinkedList<>();
iterator.forEachRemaining(result::add);

assertThat(result).containsOnly(1, 3, 5, 8);

Nachfolgende Iteration mit neu erstelltenIterator 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

DasCopyOnWriteArrayList wurde erstellt, um die Möglichkeit einer sicheren Iteration über Elemente zu ermöglichen, selbst wenn die zugrunde liegende Liste geändert wird.

Aufgrund des Kopiermechanismus ist die Operationremove() für die zurückgegebenenIterator nicht zulässig, was zuUnsupportedOperationException: führt

@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. Fazit

In diesem kurzen Tutorial haben wir uns die Implementierung vonCopyOnWriteArrayListaus dem Paket vonjava.util.concurrentangesehen.

Wir haben die interessante Semantik dieser Liste gesehen und erfahren, 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 inGitHub project - dies ist ein Maven-Projekt, daher sollte es einfach zu importieren und auszuführen sein, wie es ist.