Guia para CopyOnWriteArrayList

Guia para CopyOnWriteArrayList

1. Visão geral

Neste artigo rápido, veremosCopyOnWriteArrayList do pacotejava.util.concurrent.

Essa é uma construção muito útil nos programas multiencadeados - quando queremos iterar sobre uma lista de maneira segura para encadeamentos sem uma sincronização explícita.

2. APICopyOnWriteArrayList

O design doCopyOnWriteArrayList usa uma técnica interessante para torná-lo thread-safe sem a necessidade de sincronização. Quando estamos usando qualquer um dos métodos de modificação - comoadd() ouremove() –, todo o conteúdo deCopyOnWriteArrayList é copiado para a nova cópia interna.

Devido a este simples fato,we can iterate over the list in a safe way, even when concurrent modification is happening.

Quando chamamos o métodoiterator() emCopyOnWriteArrayList,, obtemos umIterator apoiado pelo instantâneo imutável do conteúdo deCopyOnWriteArrayList.

Seu conteúdo é uma cópia exata dos dados que estão dentro de umArrayList a partir do momento em que oIterator foi criado. Mesmo se algum outro encadeamento adicionar ou remover um elemento da lista, essa modificação fará uma nova cópia dos dados que serão usados ​​em qualquer pesquisa de dados adicional dessa lista.

As características dessa estrutura de dados a tornam particularmente útil nos casos em que a iteramos com mais frequência do que a modificada. Se adicionar elementos é uma operação comum em nosso cenário, entãoCopyOnWriteArrayList não será uma boa escolha - porque as cópias adicionais certamente levarão a um desempenho inferior.

3. Iterando sobreCopyOnWriteArrayList durante a inserção

Digamos que estejamos criando uma instância deCopyOnWriteArrayList que armazena inteiros:

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

Em seguida, queremos iterar sobre essa matriz, portanto, estamos criando uma instânciaIterator:

Iterator iterator = numbers.iterator();

Depois queIterator é criado, estamos adicionando um novo elemento à listanumbers:

numbers.add(10);

Tenha em mente que, quando criamos um iterador paraCopyOnWriteArrayList,, obtemos um instantâneo imutável dos dados na lista no momento em queiterator() foi chamado.

Por causa disso, durante a iteração, não veremos o número10 na iteração:

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

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

A iteração subsequente usandoIterator recém-criado também retornará o número 10 que foi adicionado:

Iterator iterator2 = numbers.iterator();
List result2 = new LinkedList<>();
iterator2.forEachRemaining(result2::add);

assertThat(result2).containsOnly(1, 3, 5, 8, 10);

4. A remoção durante a iteração não é permitida

OCopyOnWriteArrayList foi criado para permitir a possibilidade de iteração segura sobre os elementos, mesmo quando a lista subjacente é modificada.

Por causa do mecanismo de cópia, a operaçãoremove() noIterator retornado não é permitida - resultando emUnsupportedOperationException:

@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. Conclusão

Neste tutorial rápido, demos uma olhada na implementaçãoCopyOnWriteArrayList do pacotejava.util.concurrent.

Vimos a semântica interessante desta lista e como ela pode ser iterada de uma maneira segura para threads, enquanto outros threads podem continuar inserindo ou removendo elementos dela.

A implementação de todos esses exemplos e trechos de código pode ser encontrada emGitHub project - este é um projeto Maven, portanto, deve ser fácil de importar e executar como está.