Guida a CopyOnWriteArrayList

1. Panoramica

In questo rapido articolo, esamineremo CopyOnWriteArrayList dal pacchetto java.util.concurrent .

Questo è un costrutto molto utile nei programmi multi-thread - quando vogliamo iterare su un elenco in modo thread-safe senza una sincronizzazione esplicita.

2. API CopyOnWriteArrayList

Il design di CopyOnWriteArrayList utilizza una tecnica interessante per renderlo thread-safe senza necessità di sincronizzazione. Quando utilizziamo uno dei metodi di modifica, come add () o remove (), l'intero contenuto di CopyOnWriteArrayList viene copiato nella nuova copia interna.

A causa di questo semplice fatto, possiamo iterare l'elenco in modo sicuro, anche quando si verificano modifiche simultanee .

Quando chiamiamo il metodo iterator () su CopyOnWriteArrayList, otteniamo un Iterator supportato dall'istantanea immutabile del contenuto di CopyOnWriteArrayList .

Il suo contenuto è una copia esatta dei dati che si trovano all'interno di un ArrayList dal momento in cui è stato creato l' iteratore . Anche se nel frattempo qualche altro thread aggiunge o rimuove un elemento dalla lista, quella modifica sta facendo una nuova copia dei dati che verranno usati in ogni ulteriore ricerca di dati da quella lista.

Le caratteristiche di questa struttura dati la rendono particolarmente utile nei casi in cui la ripetiamo più spesso di quanto la stiamo modificando. Se l'aggiunta di elementi è un'operazione comune nel nostro scenario, CopyOnWriteArrayList non sarà una buona scelta, perché le copie aggiuntive porteranno sicuramente a prestazioni inferiori alla media .

3. Iterazione su CopyOnWriteArrayList durante l'inserimento

Diciamo che stiamo creando un'istanza di CopyOnWriteArrayList che memorizza i numeri interi:

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

Successivamente, vogliamo iterare su quell'array, quindi stiamo creando un'istanza di Iterator :

Iterator iterator = numbers.iterator();

Dopo aver creato l' iteratore , stiamo aggiungendo un nuovo elemento all'elenco dei numeri :

numbers.add(10);

Tieni presente che, quando creiamo un iteratore per CopyOnWriteArrayList, otteniamo un'istantanea immutabile dei dati nell'elenco al momento in cui è stato chiamato iterator () .

Per questo motivo, durante l'iterazione, non vedremo il numero 10 nell'iterazione:

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

L'iterazione successiva utilizzando l' iteratore appena creato restituirà anche il numero 10 che è stato aggiunto:

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

4. La rimozione durante l'iterazione non è consentita

Il CopyOnWriteArrayList è stato creato per consentire la possibilità di iterare in sicurezza sugli elementi anche quando l'elenco sottostante viene modificato.

A causa del meccanismo di copia, l' operazione remove () sull'iteratore restituito non è consentita, con conseguente 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. conclusione

In questo rapido tutorial, abbiamo dato uno sguardo all'implementazione CopyOnWriteArrayList dal pacchetto java.util.concurrent .

Abbiamo visto l'interessante semantica di questo elenco e come può essere iterato in modo thread-safe, mentre altri thread possono continuare a inserire o rimuovere elementi da esso.

L'implementazione di tutti questi esempi e frammenti di codice può essere trovata nel progetto GitHub: questo è un progetto Maven, quindi dovrebbe essere facile da importare ed eseguire così com'è.