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
Vector
Ele é 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 synchronized
palavras-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 add
bloqueará a execução de forma síncrona, mas a execução multithread add
e o remove
tempo 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 ArrayList
em seu lugar, mesmo se for uma situação multithread que requer uma fila sincronizada, você também pode usá-la CopyOnWriteArrayList
e SynchronizedList
substituí-la.
ArrayList
ArrayList
Ele é fornecido no JDK 1.1. Como Vector
sucessor (a ArrayList
implementação é Vector
quase a mesma), todos os ArrayList
métodos synchronized
sã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 iterator
e listIterator
criar o iterador, se a ArrayList
fila original for modificada (adicionar ou remover), ConcurrentModificationException
uma 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 modCount
nú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
SynchronizedList
Sim, Collections
uma classe interna estática, Collections.synchronizedList()
criada usando um método estático, é uma List
implementaçã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, mutex
quando o bloqueio está bloqueado, toda a fila é bloqueada, o que resolve o problema de Vector
não ser possível bloquear a fila inteira. . Portanto, se houver vários threads operando add
e remove
métodos ao mesmo tempo , ele bloqueará a execução síncrona.
ArrayList
A 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 add
seja o mesmo que o objeto no método. Isso será explicado no acompanhamento, portanto, não será expandido aqui.
CopyOnWriteArrayList
CopyOnWriteArrayList
Ele é fornecido desde o JDK 1.5, primeiro olhe add
o 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 CopyOnWriteArrayList
com a ajuda ReentrantLock
da sincronização, o desempenho é maior synchronized
antes 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.ReentrantLock
synchronized
CopyOnWriteArrayList
volatile
CopyOnWriteArrayList
add
ArrayList
get
CopyOnWriteArrayList
O iterador de é COWIterator
implementado, iterator
quando 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
CopyOnWriteArrayList
SynchronizedList
Ambos 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.
CopyOnWriteArrayList
Concentre-se na separação de leitura e gravação. Quando ocorre uma operação de gravação de dados ( add
ou 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 ( get
ou 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.
SynchronizedList
Concentre-se na consistência forte dos dados, ou seja, quando ocorre uma operação de gravação de dados ( add
ou 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 CopyOnWriteArrayList
e SynchronizedList
duas questões diferentes, pode-se concluir que CopyOnWriteArrayList
a implementação de alta eficiência, por escrito, menos leitura, mais da cena, SynchronizedList
leia 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:
Resumo no final do artigo
synchronized
O 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 foramReentrantLock
implementados.- 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
volatile
palavras - chave. ArrayList
Não há operação de sincronização e não é seguro para threadCopyOnWriteArrayList
ESynchronizedList
pertencem à fila thread-safeCopyOnWriteArrayList
Perceba a separação de leitura e escrita, adequada para cenários onde escreva menos e leia maisSynchronizedList
Os dados devem ser fortemente consistentes, que é um método de bloqueio global para a fila, e a operação de leitura também será bloqueadaVector
Acontece 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 individuaisSynchronizedList
não é muito diferente.
referência
- Por que a classe Java Vector (e Stack) é considerada obsoleta ou preterida?
- Comparação de desempenho de CopyOnWriteArrayList e Collections.synchronizedList
- Collections.synchronizedList, CopyOnWriteArrayList, introdução de vetores, análise de código-fonte e comparação de desempenho
Leitura recomendada
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