Java e Concorrência:
- Java sempre esteve na vanguarda em facilitar a programação concorrente, oferecendo suporte nativo a threads desde 1996 e evoluindo para incluir bibliotecas como java.util.concurrent e o framework fork-join.
Streams e Paralelismo:
- Com a introdução das streams no Java 8, ficou fácil paralelizar operações com uma única chamada ao método parallel(). No entanto, mesmo com a facilidade, escrever programas concorrentes corretos e rápidos continua sendo desafiador.
Desempenho e Falhas:
- Paralelizar pipelines de streams sem cuidado pode levar a falhas de desempenho e liveness (programa que não termina). Um exemplo dado mostra que paralelizar uma stream pode resultar em um aumento significativo do uso da CPU sem resultados visíveis.
Heurísticas e Limitações:
- A paralelização de streams pode falhar se a heurística usada para dividir o trabalho não for adequada, especialmente em operações como Stream.iterate e limit, onde os custos de calcular elementos extras podem ser muito altos.
Estruturas de Dados Ideais:
Streams sobre ArrayList, HashMap, HashSet, ConcurrentHashMap, arrays e ranges são melhores candidatas para paralelização devido à facilidade de divisão do trabalho entre threads e boa localidade de referência.
Operações Terminais:
- A efetividade da execução paralela também depende da operação terminal da stream. Operações de redução, como reduce, min, max, count, e sum, e operações de curto-circuito, como anyMatch, allMatch e noneMatch, são melhores para paralelismo.
Especificações Rigorosas:
- Funções usadas em pipelines paralelas devem ser associativas, não interferentes e sem estado. A violação dessas regras pode causar resultados incorretos ou falhas catastróficas.
Ordens de Execução:
- Paralelizar uma pipeline pode desordenar a saída, e operações como forEachOrdered podem ser necessárias para preservar a ordem.
Paralelismo Justificado:
- Paralelize uma stream apenas se houver uma justificativa sólida. O paralelismo inadequado pode resultar em falhas ou desempenho ruim. Sempre meça o desempenho antes e depois da paralelização para garantir que ela seja benéfica.
Exemplo de Eficácia:
- Um exemplo simples mostrou que paralelizar um cálculo de π(n) reduziu o tempo de execução de 31 para 9,2 segundos, demonstrando que o paralelismo pode ser eficiente em certos cenários.
Uso de SplittableRandom:
- Para streams de números aleatórios paralelas, prefira SplittableRandom em vez de ThreadLocalRandom ou Random, pois foi projetada especificamente para esse uso e oferece melhor desempenho.
Conclusão:
- Não tente paralelizar uma stream pipeline sem ter uma boa razão para acreditar que isso preservará a exatidão do cálculo e aumentará a velocidade. Faça testes rigorosos para verificar se o paralelismo é justificado antes de aplicá-lo em código de produção.
EXEMPLOS
1. Exemplo de Stream Sequencial vs. Paralela
- Este exemplo demonstra a diferença de desempenho entre uma stream sequencial e uma paralela. ParallelStreamExample.java
2. Exemplo de Uso Ineficiente do parallel()
- Este exemplo mostra como a paralelização pode levar a um comportamento inesperado. InefficientParallelStream.java
3. Exemplo de Uso Eficiente do parallel()
- Este exemplo mostra uma situação em que a paralelização pode realmente melhorar o desempenho. EfficientParallelStream,java
4. Exemplo de Falhas de Segurança com Streams Paralelas
- Este exemplo demonstra como uma operação de redução mal implementada pode falhar quando usada em paralelo. ParallelStreamSafetyExample.java
5. Exemplo com SplittableRandom para Streams Paralelas
- Este exemplo demonstra o uso de SplittableRandom em uma stream paralela para obter melhor desempenho. SplittableRandomExample.java
Esses exemplos ajudam a ilustrar as situações em que a paralelização pode ser útil e também mostram os riscos potenciais de usar parallel() de maneira indiscriminada.
Top comments (0)