As APIs de coleções simultâneas, além da API Java Collection, são um conjunto de APIs de coleções projetadas e otimizadas especificamente para acesso multithread sincronizado. Eles são agrupados em java.util.concurrent pacote. Este artigo fornece uma visão geral e apresenta seu uso usando um cenário de exemplo apropriado.
Uma visão geral
Java tem suportado multithreading e simultaneidade desde o seu início. Os encadeamentos são criados implementando o Runnable interface ou estendendo o Thread aula. A sincronização é conseguida pela palavra-chave chamada sincronização . Java também fornece o mecanismo de comunicação entre as threads. Isso é feito com a ajuda do notify() e esperar() métodos, que fazem parte do Object aula. Embora essas técnicas inovadoras de multithreading sejam parte de alguns dos excelentes recursos do Java, elas ficam um pouco aquém da necessidade de um programador que requer recursos multithread intensivos prontos para uso. Isso ocorre porque um programa concorrente precisa mais do que apenas ser capaz de criar threads e fazer algumas manipulações rudimentares. Ele requer muitos recursos de alto nível, como pools de threads, gerenciadores de execução, semáforos e assim por diante.
Estrutura de coleção existente
Java já tem uma estrutura de coleção completa. As coleções são muito boas no que fazem e também podem ser usadas em aplicativos de thread Java. Além disso, há uma palavra-chave, chamada sincronizado , para torná-los thread-safe. Embora superficialmente possa parecer que eles são bons para serem usados em multithreading, a forma como a segurança do thread é alcançada é o principal gargalo em sua implementação concorrente. Além da sincronização explícita, eles não são projetados sob o paradigma de implementação concorrente desde o início. A sincronização dessas coleções é feita pela serialização de todos os acessos ao estado da coleção. Isso significa que, embora possamos ter alguma simultaneidade, devido ao processamento serializado subjacente, ele funciona em um princípio que na verdade é o oposto. A serialização prejudica muito o desempenho, especialmente quando vários threads competem pelo bloqueio de toda a coleção.
Novas APIs de coleção
As APIs de coleção simultânea são uma adição ao Java da versão 5 e fazem parte do pacote chamado java.util.concurrent . Eles são uma melhoria das APIs de coleção existentes e foram projetados para acesso simultâneo de vários encadeamentos. Por exemplo, ConcurrentHashMap é na verdade a classe que precisamos quando queremos usar um Mapa baseado em hash sincronizado implementação. Da mesma forma, se quisermos uma Lista com domínio transversal e thread-safe , podemos usar a CopyOnWriterArrayList aula. O novo Mapa Concorrente interface fornece várias ações compostas em um único método, como putIfPresent , computarSePresente , substituir , mesclar , e assim por diante. Existem muitas dessas classes que estão dentro da nova estrutura de coleta simultânea. Para citar alguns:ArrayBlockingQueue , ConcurrentLinkedDeque , ConcurrentLinkedQueue , ConcurrentSkipListMap , ConcurrentSkipListSet , CopyOnWriteArraySet , DelayQueue , LinkedBlockingDeque , LinkedBlockingQueue , LinkedTransferQueue , PriorityBlockingQueue , Fila Síncrona , e outros.
Filas
Os tipos de coleção, como Fila e BlockingQueue , pode ser usado para manter um elemento temporariamente, aguardando sua recuperação na forma FIFO para processamento. ConcurrentLinkQueue , por outro lado, é uma fila FIFO tradicional implementada como uma fila sem limites e thread-safe baseada em nós vinculados. PriorityBlockingQueue é uma fila de bloqueio ilimitada que usa as mesmas normas de ordenação da PriorityQueue não concorrente e suprimentos bloqueando as operações de recuperação.
Mapas
Em classes de coleção mais antigas, quando a sincronização é aplicada, ela mantém bloqueios pela duração de cada operação. Existem operações, como o get método de HashMap ou contém método de Lista , que envolvem computação complexa nos bastidores quando invocados. Por exemplo, para encontrar um elemento específico em uma lista, ele invoca automaticamente o equals método. Este método requer certa computação para comparar cada elemento na lista; pode levar muito tempo para concluir a tarefa. Isso é pior em uma coleção baseada em hash. Se os elementos nos mapas de hash estiverem distribuídos de forma desigual, percorrer uma longa lista e chamar equals pode levar muito tempo. Isso é um problema porque pode afetar o desempenho geral do aplicativo.
Ao contrário do HashMap , ConcurrentHashMap usa uma estratégia completamente diferente. Em vez de fornecer um bloqueio comum para cada método sincronizado, ele usa uma técnica chamada remoção de bloqueio . Esta é uma solução melhor para simultaneidade e escalabilidade. A remoção de bloqueios usa bloqueios separados para buckets separados. Como resultado, a contenção de encadeamentos é dissociada da estrutura de dados subjacente e, em vez disso, imposta ao bucket. Por exemplo, a implementação de ConcurrentHashMap usa uma matriz de 16 bloqueios, cada um dos quais protege 1/16 dos baldes de hash; bucket N é protegido pelo bloqueio N mod 16… isso reduz a demanda por qualquer bloqueio em aproximadamente um fator de 16. É devido a esta técnica que ConcurrentHashMap suporta pelo menos 16 gravadores simultâneos por padrão e mais podem ser acomodados sob demanda.
CopyOnWriterArrayList
É uma boa alternativa à Lista sincronizada e não requer que você aplique um mecanismo de travamento durante a iteração. Os iteradores mantêm uma referência à matriz de apoio no início da iteração e não a alteram. Portanto, é necessária uma breve sincronização para obter o conteúdo do array. Vários encadeamentos podem acessar a coleção sem interferir um no outro. Mesmo a modificação de vários threads não sofre contenção. Existe uma contraparte definida desta lista de matrizes, chamada CopyOnWriterSet , que pode ser usado para substituir Set sincronizado na necessidade de simultaneidade.
Um exemplo rápido
Há muitas classes na coleção simultânea. Seu uso não é tão difícil para qualquer pessoa familiarizada com a estrutura de coleta mais antiga. Por uma questão de completude, aqui está um exemplo para fornecer um vislumbre de seus usos na programação Java.
package org.mano.example; import java.util.Random; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; public class ProducerConsumerDemo { static BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(5); public static void main(String[] args) throws InterruptedException { int noOfProducers = 7; int noOfConsumers = 9; for (inti = 0; i < noOfProducers; i++) { new Thread(new Producer(), "PRODUCER").start(); } for (int i = 0; i < noOfConsumers; i++) { new Thread(new Consumer(), "CONSUMER").start(); } System.exit(0); } static class Producer implements Runnable { Random random = new Random(); public void run() { try { int num = random.nextInt(100); queue.put(num); System.out.println("Produced: " + num + " Queue size : "+ queue.size()); Thread.sleep(100); } catch (InterruptedException ex) { System.out.println("Producer is interrupted."); } } } static class Consumer implements Runnable { public void run() { try { System.out.println("Consumed: " + queue.take() + " Queue size : "+ queue.size()); Thread.sleep(100); } catch (InterruptedException ex) { System.out.println("Consumer is interrupted."); } } } }
Conclusão
Talvez o maior benefício de usar as classes de coleção simultâneas seja sua escalabilidade e baixo risco. As APIs de coleção simultânea de Java fornecem uma variedade de classes que são projetadas especificamente para lidar com operações simultâneas. Essas classes são alternativas ao Java Collection Framework e fornecem funcionalidade semelhante, exceto com o suporte adicional de simultaneidade. Portanto, a curva de aprendizado para o programador que já conhece o Java Collection Framework é quase plana. As classes são definidas no pacote java.util.concurrent . Aqui, tentei dar uma visão geral para começar e usar as APIs de coleção sempre que necessário.
Referências
- Documentação da API Java
- Goetz, Brian e Tim Peierls. Simultaneidade Java na prática . Pearson, 2013.