O entrevistador pergunta o que é COW
"Droga? Por que você xinga quando eu venho para uma entrevista?".
Entrevistador: "Não, não é esse. É a VACA inglesa."
"......vaca?".
O entrevistador ficou com vergonha ... "Você sabe que você copia o container enquanto escreve?"
Através deste pequeno exemplo (não real), aprofundarei sua impressão sobre esta abreviatura.
O Copy-On-Write é conhecido como COW. O entendimento popular é que quando adicionamos elementos a um contêiner, não adicionamos diretamente ao contêiner atual, mas primeiro copiamos o contêiner atual para copiar um novo contêiner e, em seguida, adicionamos elementos ao novo contêiner. Depois de adicionar os elementos, A seguir, aponte a referência do contêiner original para o novo contêiner. A vantagem disso é que podemos ler o contêiner CopyOnWrite simultaneamente sem travar, porque o contêiner atual não adicionará nenhum elemento. Portanto, o container CopyOnWrite também é uma ideia de separação entre leitura e escrita, com diferentes containers para leitura e escrita. A partir do JDK1.5, o Java Concurrency Package fornece dois contêineres simultâneos implementados usando o mecanismo CopyOnWrite: CopyOnWriteArrayList e CopyOnWriteArraySet.
Código do núcleo
conjunto E público (índice interno , elemento E) { final ReentrantLock lock = this.lock; lock.lock (); // Obtenha o bloqueio try { Object [] elements = getArray (); // Obtenha uma cópia do contêiner atual array E oldValue = get (elements, index); // Obtenha o valor atual do elemento correspondente à posição do índice if (oldValue! = element) { int len = elements.length; // Crie um novo array newElements e copie os elementos sobre Object [] newElements = Arrays.copyOf (elements, len); // Substitua o elemento na posição do índice no novo array pelo elemento newElements [index] = element; // Esta etapa é a chave, a função é para apontar a referência da matriz no contêiner para a matriz modificada, ou seja, newElements setArray (newElements); } else { // O valor do elemento na posição do índice é igual ao do elemento, então a matriz do contêiner não é modificada setArray (elements); } return oldValue; } finally { lock.unlock (); // Desbloquear } }
Podemos ver que no método set, primeiro obtemos uma cópia do array atual para obter um novo array e, em seguida, concluímos a operação que queremos neste novo array. Quando a operação é concluída, a referência do array original é apontada para o novo array.
Durante esse processo, descobrimos que um bloqueio foi adicionado. O motivo é que o processo de cópia da matriz não é uma operação atômica, portanto, a separação entre gravação e gravação é necessária.
Em contraste com as operações de leitura, quando a operação de gravação é concluída, a referência do array original ao novo array pode ser considerada uma operação atômica. Portanto, não há necessidade de bloquear nenhuma operação de leitura e é garantido que todos os dados instantâneos da Lista no momento da leitura sejam lidos.
Não importa como a própria Lista muda, o que o iterador pode perceber é o estado da Lista no momento em que ela é criada. Quaisquer outras alterações de thread na Lista são invisíveis para este iterador. Não haverá nenhuma situação em que o iterador de ConcurrentHashMap possa ler o estado intermediário do contêiner durante o processo de modificação de outros encadeamentos. Como a operação de leitura CopyOnWriteArrayList não pode perceber os dados de alteração mais recentes, CopyOnWriteArrayList também é fracamente consistente como ConcurrentHashMap.
Resumindo
Vamos comparar o ConcurrentHashMap de contêiner simultâneo mais comum para resumir:
- ConcurrentHashMap e CopyOnWriteArrayList são leituras sem bloqueio, portanto, quando ocorre uma operação de leitura, não é possível garantir que as operações de gravação atuais de todos os outros threads tenham sido concluídas e não podem ser usadas em cenários que exigem consistência forte de dados.
- Tanto ConcurrentHashMap quanto CopyOnWriteArrayList podem garantir que a operação de gravação concluída possa ser detectada durante a leitura.
- As operações de leitura de ConcurrentHashMap podem perceber o estado intermediário de outras threads gravando no contêiner ao mesmo tempo. CopyOnWriteArrayList sempre lerá apenas o estado do instantâneo do contêiner no momento da leitura.
- ConcurrentHashMap usa tecnologia de segmentação de bloqueio para reduzir o escopo dos bloqueios e aumentar a quantidade de gravações simultâneas. CopyOnWriteArrayList usa a tecnologia copy-on-write para garantir que os dados sejam gravados simultaneamente sem causar interferência nas operações de leitura que foram abertas. Não é difícil descobrir que a granularidade de bloqueio de CopyOnWriteArrayList é muito grande, portanto, no cenário de simultaneidade excessiva, a eficiência de gravação é muito baixa.
- ConcurrentHashMap é adequado para cenários em que não há um requisito de consistência forte para acesso a dados em alta simultaneidade. CopyOnWriteArrayList é adequado para cenários com forte acoplamento de elemento em contêineres simultâneos, o que pode garantir que os dados em cada instantâneo sejam consistentes. Se houver muitos elementos no contêiner simultâneo, o custo da memória de cópia será muito alto e a eficiência da cópia será reduzida ainda mais.
No desenvolvimento real, você pode escolher um contêiner simultâneo de acordo com o cenário de negócios.