Conheça as filas em Java: Vector, ArrayList, CopyOnWriteArrayList, SynchronizedList

Conheça as filas em Java: Vector, ArrayList, CopyOnWriteArrayList, SynchronizedList

Olá, estou observando as montanhas.

Livro conectado a Wen, último bate-papo usado em vários threads ArrayList o que vai acontecer , desta vez geralmente falamos sobre lista comum: Vector, ArrayList, CopyOnWriteArrayList, SynchronizedList .

Vetor

VectorEle é fornecido no JDK 1.0, embora não esteja marcado Deprecated, na verdade ninguém o usa mais. O principal motivo é o baixo desempenho e não atende à demanda.

Como você pode ver no código-fonte (o código-fonte não é postado aqui), ele Vectoré baseado em uma implementação de array. Quase todos os métodos de operação usam synchronizedpalavras-chave para alcançar a sincronização do método. Este método de sincronização pode bloquear uma única operação, como vários threads ao mesmo tempo. A execução addbloqueará a execução de forma síncrona, mas a execução multithread adde o removetempo não serão bloqueados.

No entanto, a maioria dos cenários em que você precisa bloquear a fila é para bloquear a fila inteira, não apenas uma única operação. Em outras palavras, Vectoré diferente de nossa expectativa, mas também aumenta a sobrecarga de desempenho causada pela operação de sincronização. Portanto, não é necessário usar a cena, você pode usá-la ArrayListem seu lugar, mesmo se for uma situação multithread que requer uma fila sincronizada, você também pode usá-la CopyOnWriteArrayListe SynchronizedListsubstituí-la.

ArrayList

ArrayListEle é fornecido no JDK 1.1. Como Vectorsucessor (a ArrayListimplementação é Vectorquase a mesma), todos os ArrayListmétodos synchronizedsão removidos, a sincronização não é alcançada e não é seguro para threads.

Sua segurança não thread também se reflete na falha rápida dos iteradores. Depois de usar o método iteratore listIteratorcriar o iterador, se a ArrayListfila original for modificada (adicionar ou remover), ConcurrentModificationExceptionuma exceção será relatada quando o iterador estiver iterando . Pode-se ver no código-fonte que durante o processo de iteração do iterador, ele verifica se o modCountnúmero de modificações na fila expectedModCounté igual ao instantâneo do número de modificações que caíram quando o iterador foi criado . Igualdade significa que ele não foi modificado. O código é o seguinte:

private class Itr implements Iterator<E> {
    
    
    // 这段代码是从 ArrayList 中摘取的
    // 只留下检查方法,略过其他代码,有兴趣的可以从源码中查看
    final void checkForComodification() {
    
    
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

O terceiro ponto é que em um cenário multi-threaded, adicionar elementos pode perder dados ou ocorre uma exceção de array fora dos limites . O que vai acontecer ao usar ArrayList em multi-threaded é descrito em detalhes, então não vou entrar em detalhes aqui.

SynchronizedList

SynchronizedListSim, Collectionsuma classe interna estática, Collections.synchronizedList()criada usando um método estático, é uma Listimplementação encapsulada realizada por uma classe composta . A maioria de seus métodos usa synchronized (mutex){...}sincronização de bloco de código, porque o objeto bloqueado mutexé o mesmo objeto definido no objeto fila, portanto, mutexquando o bloqueio está bloqueado, toda a fila é bloqueada, o que resolve o problema de Vectornão ser possível bloquear a fila inteira. . Portanto, se houver vários threads operando adde removemétodos ao mesmo tempo , ele bloqueará a execução síncrona.

ArrayListA falha rápida do iterador na situação existente ainda existe, conforme a observação no código-fonte abaixo: Se você deseja usar o iterador, é necessário implementar a sincronização manualmente.

static class SynchronizedList<E>
    extends SynchronizedCollection<E>
    implements List<E> {
    
    
    
    // 代码摘自 Collections,省略很多代码

    public void add(int index, E element) {
    
    
        synchronized (mutex) {
    
    list.add(index, element);}
    }

    public ListIterator<E> listIterator() {
    
    
        return list.listIterator(); // Must be manually synched by user
    }

    public ListIterator<E> listIterator(int index) {
    
    
        return list.listIterator(index); // Must be manually synched by user
    }
}

Você precisa prestar atenção à sincronização manual. Como estamos preocupados com a sincronização global, ao definir a sincronização no iterador, certifique-se de que o objeto bloqueado addseja o mesmo que o objeto no método. Isso será explicado no acompanhamento, portanto, não será expandido aqui.

CopyOnWriteArrayList

CopyOnWriteArrayListEle é fornecido desde o JDK 1.5, primeiro olhe addo código-fonte do método:

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    
    

    /** The lock protecting all mutators */
    final transient ReentrantLock lock = new ReentrantLock();

    /** The array, accessed only via getArray/setArray. */
    private transient volatile Object[] array;

    // 代码摘自 CopyOnWriteArrayList,省略很多代码

    public boolean add(E e) {
    
    
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
    
    
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
    
    
            lock.unlock();
        }
    }

    public boolean addAll(Collection<? extends E> c) {
    
    
        Object[] cs = (c.getClass() == CopyOnWriteArrayList.class) ?
            ((CopyOnWriteArrayList<?>)c).getArray() : c.toArray();
        if (cs.length == 0)
            return false;
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
    
    
            Object[] elements = getArray();
            int len = elements.length;
            if (len == 0 && cs.getClass() == Object[].class)
                setArray(cs);
            else {
    
    
                Object[] newElements = Arrays.copyOf(elements, len + cs.length);
                System.arraycopy(cs, 0, newElements, len, cs.length);
                setArray(newElements);
            }
            return true;
        } finally {
    
    
            lock.unlock();
        }
    }

    private E get(Object[] a, int index) {
    
    
        return (E) a[index];
    }

    /**
     * {@inheritDoc}
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E get(int index) {
    
    
        return get(getArray(), index);
    }
}

Pode-se ver que CopyOnWriteArrayListcom a ajuda ReentrantLockda sincronização, o desempenho é maior synchronizedantes da otimização . Também é implementado por meio de uma matriz, mas uma palavra-chave é adicionada na frente da matriz , o que percebe a visibilidade da matriz no caso de multi-threading e é mais segura. O ponto mais importante é que, ao adicionar elementos, a implementação é reconstruir o objeto de matriz e substituir a referência de matriz original. Em comparação com o método de expansão, o espaço é reduzido, mas a sobrecarga de desempenho da matriz de atribuição também é aumentada. Quando o elemento é adquirido, não há bloqueio e os dados são retornados diretamente.ReentrantLocksynchronizedCopyOnWriteArrayListvolatileCopyOnWriteArrayListaddArrayListget

CopyOnWriteArrayListO iterador de é COWIteratorimplementado, iteratorquando o método é chamado , o instantâneo do array na fila atual é atribuído à referência de array no iterador. Se a fila original for modificada, a matriz na fila apontará para outras referências, e a matriz no iterador não mudará, portanto, no processo de execução multithread, os dados na fila também podem ser modificados percorrendo o array por meio do iterador. Enquanto garante a segurança do thread dessa forma, inconsistências de dados também podem ocorrer, e você só pode prestar mais atenção ao seu uso.

static final class COWIterator<E> implements ListIterator<E> {
    
    
    /** Snapshot of the array */
    private final Object[] snapshot;
    /** Index of element to be returned by subsequent call to next.  */
    private int cursor;

    private COWIterator(Object[] elements, int initialCursor) {
    
    
        cursor = initialCursor;
        snapshot = elements;
    }
}

Compare CopyOnWriteArrayList e SynchronizedList

CopyOnWriteArrayListSynchronizedListAmbos e ambos são sincronizados, e diferentes estratégias são adotadas no método de execução, e seus respectivos focos são diferentes.

CopyOnWriteArrayListConcentre-se na separação de leitura e gravação. Quando ocorre uma operação de gravação de dados ( addou remove), ela será bloqueada e cada thread bloqueará a execução. O processo de execução criará uma cópia de dados e substituirá a referência do objeto; se houver um operação de leitura ( getou iterator) ao mesmo tempo , a operação de leitura lê Dados antigos podem se tornar instantâneos de dados históricos ou dados em cache. Isso causará inconsistência de dados quando a leitura e a gravação ocorrerem ao mesmo tempo, mas os dados acabarão por ser consistentes. Este método é quase igual ao modo de separação de leitura e gravação do banco de dados e muitos recursos podem ser comparados.

SynchronizedListConcentre-se na consistência forte dos dados, ou seja, quando ocorre uma operação de gravação de dados ( addou remove), um bloqueio será adicionado, cada thread bloqueará a execução e a operação também será bloqueada pelo mesmo bloqueio get.

A partir CopyOnWriteArrayListe SynchronizedListduas questões diferentes, pode-se concluir que CopyOnWriteArrayLista implementação de alta eficiência, por escrito, menos leitura, mais da cena, SynchronizedListleia e eficiência as operações de gravação é muito equilibrado, por isso escrita para ler mais, Write Once Read Many cenas pequenas eficiência será mais alto CopyOnWriteArrayList. Peça os resultados do teste pela Internet:

Compare CopyOnWriteArrayList e SynchronizedList

Resumo no final do artigo

  1. synchronizedO desempenho das palavras-chave é relativamente baixo antes do JDK 8. Você pode ver o código de sincronização implementado após o JDK 1.5, muitos dos quais foram ReentrantLockimplementados.
  2. Em cenários multithread, além da sincronização, a visibilidade dos dados também precisa ser considerada, o que pode ser obtido por meio de volatilepalavras - chave.
  3. ArrayListNão há operação de sincronização e não é seguro para thread
  4. CopyOnWriteArrayListE SynchronizedListpertencem à fila thread-safe
  5. CopyOnWriteArrayListPerceba a separação de leitura e escrita, adequada para cenários onde escreva menos e leia mais
  6. SynchronizedListOs dados devem ser fortemente consistentes, que é um método de bloqueio global para a fila, e a operação de leitura também será bloqueada
  7. VectorAcontece que o desempenho de passagem do iterador é muito ruim. Se você não considerar a fila de bloqueio global, o desempenho de operações de leitura pura e operações de gravação individuais SynchronizedListnão é muito diferente.

referência

  1. Por que a classe Java Vector (e Stack) é considerada obsoleta ou preterida?
  2. Comparação de desempenho de CopyOnWriteArrayList e Collections.synchronizedList
  3. Collections.synchronizedList, CopyOnWriteArrayList, introdução de vetores, análise de código-fonte e comparação de desempenho

Leitura recomendada

  1. O que acontece se você tiver que usar ArrayList em vários threads?

Olá, sou Kanshan, conta pública: casa de campo de Kanshan, back-end de 10 anos, Apache Storm, WxJava, contribuidor de código aberto Cynomys. Trabalho principal: programador, trabalho a tempo parcial: arquitecto. Nade no mundo do código, aproveite a vida no drama.

Página pessoal: https://www.howardliu.cn
Postagem do blog pessoal: Conheça as filas em Java: Vector, ArrayList, CopyOnWriteArrayList, SynchronizedList
Página inicial do CSDN: http://blog.csdn.net/liuxinghao
Postagem do blog do CSDN: Conheça as filas em Java: Vector, ArrayList, CopyOnWriteArrayList, SynchronizedList

Número público: Vigiando a cabana na montanha

Acho que você gosta

Origin blog.csdn.net/conansix/article/details/113780875
Recomendado
Clasificación