Programação Paralela e assíncrona em Java 8

código paralelo, que é um código que corre em mais de um fio, foi o pesadelo de muitos desenvolvedores experientes, mas Java 8 trouxe um monte de mudanças que devem tornar este truque de impulsionar o desempenho muito mais gerenciável.

fluxos paralelos

Antes de Java 8 havia uma grande diferença entre código paralelo (ou concorrente) e Código sequencial. Também foi muito difícil depurar código Não Sequencial. Simplesmente definir um ponto de paragem e passar pelo fluxo como você normalmente faria, removeria o aspecto paralelo, que é um problema Se é isso que está causando o bug.

felizmente, Java 8 nos deu streams, a melhor coisa para desenvolvedores Java desde o bean. Se você não sabe o que eles são, a API Stream torna possível lidar com sequências de elementos em uma matéria funcional. (Verifique nossa comparação entre fluxos e LINQ. net aqui.) Uma das vantagens dos fluxos é que a estrutura do código permanece a mesma: seja sequencial ou concomitante, permanece igualmente legível.

para fazer o seu código correr paralelo, você simplesmente usa .parallelStream() em vez de .stream(), (ou stream.parallel(), se você não é o criador do fluxo).

mas só porque é fácil, não significa que o código paralelo é sempre a melhor escolha. Você deve sempre considerar se faz algum sentido usar a concorrência para o seu pedaço de código. O fator mais importante nessa decisão será a velocidade: só usar a concorrência se ela tornar o seu código mais rápido do que a sua contraparte sequencial.

the Speed Question

Parallel code gets its speed benefit from using multiple threads instead of the single that sequential code uses. Decidir quantos threads Criar pode ser uma questão complicada, porque mais threads nem sempre resultam em código mais rápido: se você usar muitos threads o desempenho de seu código pode realmente ir para baixo.

existem algumas regras que lhe dirão qual o número de tópicos a escolher. Isso depende principalmente do tipo de operação que você quer realizar e do número de núcleos disponíveis.

as operações intensivas de computação devem usar um número de threads menor ou igual ao número de núcleos, enquanto as operações intensivas de IO como arquivos de cópia não têm uso para a CPU e, portanto, pode usar um número maior de threads. O código não sabe qual é o caso aplicável a menos que lhe diga o que fazer. Caso contrário, será padrão para um número de threads igual ao número de núcleos.

existem dois casos principais em que pode ser útil executar o seu código em paralelo em vez de sequencial: tarefas e tarefas demoradas são executadas em grandes coleções. Java 8 trouxe uma nova maneira de lidar com essas grandes coleções, nomeadamente com fluxos. Os fluxos têm eficiência incorporada por preguiça: eles usam avaliação preguiçosa que economiza recursos por não fazer mais do que o necessário. Isto não é o mesmo que o paralelismo, que não se importa com os recursos desde que vá mais rápido. Então, para grandes coleções, você provavelmente não precisa de paralelismo clássico.

Vai Assíncrono

Aulas De JavaScript

é uma ocorrência rara que um programador Java pode-se dizer que aprendeu alguma coisa de olhar para o JavaScript, mas quando se trata de programação assíncrona, JavaScript, na verdade, deu certo de primeira. Como uma linguagem fundamentalmente async, JavaScript tem muita experiência com o quão doloroso pode ser quando mal implementado. Começou com callbacks e mais tarde foi substituído por promessas. Um benefício importante das promessas é que ele tem dois” canais”: um para dados e outro para erros. Uma promessa de JavaScript pode parecer-se com isto.:

func.then(f1).catch(e1).then(f2).catch(e2);

então, quando a função original tem um resultado bem sucedido, f1 é chamado, mas se um erro foi lançado e1 será chamado. Isto pode trazê-lo de volta para a faixa de sucesso (f2), ou resultar em outro erro (e2). Você pode ir de faixa de dados para faixa de erro e voltar.

a versão Java das promessas de JavaScript é chamada de CompletableFuture.

o futuro completo

CompletableFuture implementa tanto a interface Future como a interface CompletionStage. Future já existia antes do Java8, mas não era muito amigável com o desenvolvedor por si só. Você só poderia obter o resultado da computação assíncrona usando o método .get(), que bloqueou o resto (tornando a parte async bastante inútil A maior parte do tempo) e você precisava implementar cada cenário possível manualmente. Adicionando a interface CompletionStage foi o avanço que fez a programação assíncrona em Java viável.

CompletionStage é uma promessa, ou seja, a promessa de que a computação será eventualmente feita. Ele contém um monte de métodos que lhe permitem anexar callbacks que serão executados nessa conclusão. Agora podemos lidar com o resultado sem bloquear.

Existem dois métodos principais que permitem que você iniciar o assíncrono parte do código: supplyAsync se você quer fazer algo com o resultado do método, e runAsync se você não.

CompletableFuture.runAsync(() → System.out.println("Run async in completable future " + Thread.currentThread()));CompletableFuture.supplyAsync(() → 5);

Retornos de chamada de

Agora você pode adicionar esses retornos de chamada para manipular o resultado do seu supplyAsync.

CompletableFuture.supplyAsync(() → 5).thenApply(i → i * 3).thenAccept(i → System.out.println("The result is " + i).thenRun(() → System.out.println("Finished."));

.thenApply é semelhante à função .map para fluxos: ele realiza uma transformação. No exemplo acima toma o resultado (5) e multiplica-o por 3. Ele então passará esse resultado (15) mais adiante no tubo.

.thenAccept executa um método sobre o resultado sem o transformar. Também não vai devolver um resultado. Aqui vai imprimir “o resultado é 15” para o console. Ele pode ser comparado ao método .foreach para córregos.

.thenRun não utiliza o resultado da operação async e também não devolve nada, apenas espera chamar o seu Runnable até que o passo anterior seja concluído.

Asynting Your Async

All of the above callback methods also come in an async version: thenRunAsync, thenApplyAsync, etc. Estas versões podem rodar no seu próprio tópico e dão-lhe um controlo extra porque pode dizer-lhe qual ForkJoinPool usar.

se você não usar a versão async, então os callbacks serão todos executados no mesmo tópico.

When Things Go Wrong

When something goes wrong, the exceptionally method is used to handle the exception. Você pode dar-lhe um método que retorna um valor para voltar à faixa de dados, ou lançar uma (nova) exceção.

….exceptionally(ex → new Foo()).thenAccept(this::bar);

Combine e componha

pode cadeia múltipla CompletableFutures usando o método thenCompose. Sem ele, o resultado seria aninhado CompletableFutures. Isso faz com que thenCompose e thenApply como flatMap e map para córregos.

CompletableFuture.supplyAsync(() -> "Hello").thenCompose(s -> CompletableFuture.supplyAsync(() -> s + "World"));

se você quiser combinar o resultado de dois CompletableFutures, você vai precisar de um método convenientemente chamado thenCombine.

future.thenCombine(future2, Integer::sum).thenAccept(value → System.out.println(value));

como pode ver no exemplo acima, o resultado do callback em thenCombine pode ser tratado como um normal CompletableFuture com todos os seus métodos favoritos CompletionStage.

Conclusion

Parallel programming no longer needs to be an insurmountable obstacle in the hunt for faster code. Java 8 torna o processo tão simples quanto possível, de modo que qualquer pedaço de código que possa se beneficiar dele, pode ser puxado, chutando e gritando em todos os threads, para o futuro multi-núcleo que é, de fato, apenas o dia atual. O que eu quero dizer: é fácil de fazer, então dê-lhe uma tentativa e veja suas vantagens por si mesmo.



+