Contêiner de cópia na gravação de programação simultânea Java

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:

  1. 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.
  2. Tanto ConcurrentHashMap quanto CopyOnWriteArrayList podem garantir que a operação de gravação concluída possa ser detectada durante a leitura.
  3. 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.
  4. 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.
  5. 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.

Acho que você gosta

Origin blog.csdn.net/weixin_47184173/article/details/115269145
Recomendado
Clasificación