Como chamar de forma assíncrona um método em Java
Ultimamente, tenho visto as goroutines do Go e achei que seria bom ter algo semelhante em Java. Tanto quanto eu procurei a maneira comum de paralelizar uma chamada de método é fazer algo como:
final String x = "somethingelse";
x.matches("something");
Isso não é muito elegante. Existe um jeito melhor de fazer isso? Como eu precisava dessa solução em um projeto, decidi implementar minha própria classe de wrapper em torno de uma chamada de método assíncrona.
Publiquei minha classe de wrapper no J-Go . Mas não sei se é uma boa solução. O uso é simples:
SampleClass obj = ...
FutureResult<Integer> res = ...
Go go = new Go(obj);
go.callLater(res, "intReturningMethod", 10);         //10 is a Integer method parameter
System.out.println("Result: "+res.get());           //Blocks until intReturningMethod returns
ou menos detalhado:
Go.with(obj).callLater("myRandomMethod");
//... Go away
if (Go.lastResult().isReady())                //Blocks until myRandomMethod has ended
System.out.println("Method is finished!");
Internamente, estou usando uma classe que implementa Runnable e faço algum trabalho de reflexão para obter o objeto de método correto e invocá-lo.
Eu quero uma opinião sobre minha pequena biblioteca e sobre o assunto de fazer chamadas de método assíncronas como esta em Java. É seguro? Já existe uma maneira mais simples?
java concurrency asynchronous goroutine
— Felipe Hummel
Você pode mostrar seu código de J-Go lib novamente?
Eu estava trabalhando em um projeto em que estava lendo um fluxo de caracteres. Depois que uma palavra completa é lida, eu estava realizando muitas operações nessa palavra. Finalmente, coloquei-o em uma coleção. Depois que todos os dados do fluxo forem lidos, retornarei a resposta. Decidi iniciar o thread para cada vez que realizo uma operação em uma palavra. Mas diminuiu o desempenho geral. Então eu fiquei sabendo que os threads são em si uma operação cara. Não tenho certeza se o início de um thread para chamar um método simultaneamente pode dar um acréscimo de desempenho até que ele esteja executando qualquer operação pesada de E / S.
Ótima pergunta !! O Java 8 fornece CompletableFutures para isso. Outras respostas são baseados em versões provavelmente antigas do Java
Acabei de descobrir que existe uma maneira mais limpa de fazer sua
(Pelo menos no Java 8), você pode usar uma expressão lambda para reduzi-la para:
Tão simples quanto criar uma função em JS!
— AegisHexad
Sua resposta ajudou no meu problema - stackoverflow.com/questions/27009448/… . Pouco complicado para aplicar o meu situatioin, mas trabalhou isso eventualmente :)
Como chamar com parâmetros?
— yatanadam
@yatanadam Isso pode responder à sua pergunta. Basta colocar o código acima dentro de um método e colar a variável como faria normalmente. Confira este código de teste que fiz para você.
— user3004449
@eNnillaMS O thread precisa ser interrompido após a execução? Ele para automaticamente ou pelo coletor de lixo ?
@ user3004449 A discussão para automaticamente, no entanto, você também pode forçá-la.
O Java 8 introduziu o CompletableFuture disponível no pacote java.util.concurrent.CompletableFuture, pode ser usado para fazer uma chamada assíncrona:
// method call or code to be asynch.
— Rahul Chauhan
CompletableFuturefoi mencionado em outra resposta, mas essa usou toda a supplyAsync(...)cadeia. Este é um invólucro simples que se encaixa perfeitamente na pergunta.
— Ndm13 5/02
ForkJoinPool.commonPool().execute()tem um pouco menos despesas gerais
— Mark Jeronimus 28/08
Você também pode considerar a classe java.util.concurrent.FutureTask.
Se você estiver usando o Java 5 ou posterior, o FutureTask é uma implementação chave na mão de "Uma computação assíncrona cancelável".
Existem comportamentos de agendamento de execução assíncrona ainda mais ricos disponíveis no java.util.concurrentpacote (por exemplo ScheduledExecutorService), mas FutureTaskpodem ter toda a funcionalidade necessária.
Eu chegaria ao ponto de dizer que não é mais aconselhável usar o primeiro padrão de código que você deu como exemplo desde que FutureTaskse tornou disponível. (Supondo que você esteja no Java 5 ou posterior.)
— shadit
O problema é que eu só quero executar uma chamada de método. Dessa forma, eu teria que mudar a implementação da classe de destino. A coisa que eu queria é exatamente chamada sem ter que se preocupar sobre como implementar Runnable ou mobilizável
Eu te escuto. Java ainda não possui funções de primeira classe, portanto este é o estado da arte no momento.
— Shadit 04/12/2009
obrigado pelas palavras-chave 'future' ... agora estou abrindo os tutoriais sobre elas ... muito úteis. : D
-1, pelo que sei, o FutureTask por si só não é suficiente para executar nada de forma assíncrona. Você ainda precisa criar um Thread ou Executor para executá-lo, como no exemplo de Carlos.
— MikeFHay
não gosto da ideia de usar o Reflection para isso.
Não é apenas perigoso por faltar em alguma refatoração, mas também pode ser negado SecurityManager.
FutureTaské uma boa opção, como as outras opções do pacote java.util.concurrent.
O meu favorito para tarefas simples:
Executors.newSingleThreadExecutor().submit(task);
um pouco mais curto que a criação de um Thread (a tarefa é Callable ou Runnable)
então esta ajuda Would'nt muito :( mas normalmente eu preferiria usar um Runnable ou mobilizável em vez de Reflexão
Apenas uma breve observação: é melhor acompanhar a ExecutorServiceinstância, para que você possa ligar shutdown()quando precisar.
— Daniel Szalay
Se você fizesse isso, você não acabaria com o ExecutorService não fechado, fazendo com que sua JVM se recusasse a encerrar? Eu recomendaria escrever seu próprio método para obter um ExecutorService de thread único e com tempo limitado.
Você pode usar a sintaxe Java8 para CompletableFuture, desta forma, poderá executar cálculos assíncronos adicionais com base no resultado da chamada de uma função assíncrona.
CompletableFuture.supplyAsync(this::findSomeData)
.thenApply(this:: intReturningMethod)
.thenAccept(this::notify);
Mais detalhes podem ser encontrados neste artigo
— Tal Avissar
Você pode usar a @Asyncanotação em jcabi-aspect e AspectJ:
// to be executed in the background
Quando você chama save(), um novo thread inicia e executa seu corpo. Seu segmento principal continua sem aguardar o resultado de save().
Observe que essa abordagem faria todas as chamadas para salvar assíncronas, que podem ou não ser o que queremos. Nos casos em que apenas algumas chamadas para um determinado método devem ser feitas de forma assíncrona, outras abordagens sugeridas nesta página podem ser usadas.
— bmorin
Posso usá-lo em um aplicativo Web (já que o gerenciamento de threads não é recomendado lá)?
— Onlinenaman
Você pode usar o Future-AsyncResult para isso.
Referência: https://spring.io/guides/gs/async-method/
— user3177227
se você estiver usando boot de mola.
Java também fornece uma boa maneira de chamar métodos assíncronos. em java.util.concurrent , temos ExecutorService que ajuda a fazer o mesmo. Inicialize seu objeto assim -
private ExecutorService asyncExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
e depois chame a função como
asyncExecutor.execute(() -> {
TimeUnit.SECONDS.sleep(3L);}
Provavelmente não é uma solução real, mas agora - no Java 8 - você pode fazer esse código parecer um pouco melhor usando a expressão lambda.
E você pode até fazer isso em uma linha, mantendo-a bem legível.
new Thread(() -> x.matches("something")).start();
— kcpr
É muito bom para usar esta usando Java 8.
Você pode usar AsyncFuncdo Cactoos :
boolean matches = new AsyncFunc(
x -> x.matches("something")
).apply("The text").get();
Ele será executado em segundo plano e o resultado estará disponível em get()a Future.
Isso não está realmente relacionado, mas se eu chamasse um método de forma assíncrona, por exemplo, match (), usaria:
private final static ExecutorService service = Executors.newFixedThreadPool(10);
public static Future<Boolean> matches(final String x, final String y) {
return service.submit(new Callable<Boolean>() {
return x.matches(y);
Então, para chamar o método assíncrono, eu usaria:
String x = "somethingelse";
System.out.println("Matches: "+matches(x, "something").get());
Eu testei isso e funciona. Apenas pensei que poderia ajudar os outros se eles viessem para o "método assíncrono".
— FlameBlazer
Também existe uma biblioteca agradável para o Async-Await criada pela EA: https://github.com/electronicarts/ea-async