web-dev-qa-db-fra.com

Comment appeler de manière asynchrone une méthode dans Java

Je suis en train de regarder les goroutines de Go récemment et j'ai pensé qu'il serait agréable d'avoir quelque chose de similaire en Java. Dans la mesure où j'ai recherché la méthode la plus courante pour paralléliser un appel de méthode, procédez comme suit:

final String x = "somethingelse";
new Thread(new Runnable() {
           public void run() {
                x.matches("something");             
    }
}).start();

Ce n'est pas très élégant. Y a-t-il un meilleur moyen de faire cela? Comme j'avais besoin d'une telle solution dans un projet, j'ai donc décidé d'implémenter ma propre classe de wrapper autour d'un appel de méthode asynchrone.

J'ai publié ma classe wrapper dans J-Go . Mais je ne sais pas si c'est une bonne solution. L'utilisation est simple:

SampleClass obj = ...
FutureResult<Integer> res = ...
Go go = new Go(obj);
go.callLater(res, "intReturningMethod", 10);         //10 is a Integer method parameter
//... Do something else
//...
System.out.println("Result: "+res.get());           //Blocks until intReturningMethod returns

ou moins verbeux:

Go.with(obj).callLater("myRandomMethod");
//... Go away
if (Go.lastResult().isReady())                //Blocks until myRandomMethod has ended
    System.out.println("Method is finished!");

En interne, j'utilise une classe qui implémente Runnable et effectue des travaux de réflexion pour obtenir le bon objet de méthode et l'appeler.

Je veux des opinions sur ma minuscule bibliothèque et sur le fait de faire des appels de méthodes asynchrones comme celui-ci en Java. Est-ce sûr? Y a-t-il déjà un moyen plus simple?

97
Felipe Hummel

Je viens de découvrir qu’il existe un moyen plus propre de faire votre

new Thread(new Runnable() {
    public void run() {
        //Do whatever
    }
}).start();

(Au moins dans Java 8), vous pouvez utiliser une expression lambda pour la raccourcir afin de:

new Thread(() -> {
    //Do whatever
}).start();

Aussi simple que de créer une fonction dans JS!

137
AegisHexad

Java 8 introduit CompletableFuture disponible dans le package Java.util.concurrent.CompletableFuture, peut être utilisé pour effectuer un appel asynchrone:

CompletableFuture.runAsync(() -> {
    // method call or code to be asynch.
});
32
Rahul Chauhan

Vous voudrez peut-être aussi considérer la classe Java.util.concurrent.FutureTask.

Si vous utilisez Java 5 ou une version ultérieure, FutureTask est une implémentation clé en main de "Un calcul asynchrone annulable."

Il existe même des comportements de planification d'exécution asynchrone plus riches disponibles dans le package Java.util.concurrent (Par exemple, ScheduledExecutorService), mais FutureTask peut disposer de toutes les fonctionnalités dont vous avez besoin.

J'irais même jusqu'à dire qu'il n'est plus conseillé d'utiliser le premier modèle de code que vous avez donné comme exemple depuis que FutureTask est devenu disponible. (En supposant que vous êtes sur Java 5 ou une version ultérieure.)

31
shadit

je n'aime pas l'idée d'utiliser Reflection pour cela.
Non seulement dangereux de ne pas l'avoir dans certaines refactorisations, il peut aussi être refusé par SecurityManager.

FutureTask est une bonne option comme les autres options du paquet Java.util.concurrent.
Mon préféré pour les tâches simples:

    Executors.newSingleThreadExecutor().submit(task);

pe peu plus long que la création d'un thread (la tâche est un appelable ou un exécutable)

23
Carlos Heuberger

Vous pouvez utiliser la syntaxe Java8 pour CompletableFuture afin d'effectuer d'autres calculs asynchrones en fonction du résultat de l'appel d'une fonction asynchrone.

par exemple:

 CompletableFuture.supplyAsync(this::findSomeData)
                     .thenApply(this:: intReturningMethod)
                     .thenAccept(this::notify);

Plus de détails peuvent être trouvés dans ceci article

11
Tal Avissar

Vous pouvez utiliser @Async l'annotation de jcabi-aspects et AspectJ:

public class Foo {
  @Async
  public void save() {
    // to be executed in the background
  }
}

Lorsque vous appelez save(), un nouveau thread démarre et exécute son corps. Votre thread principal continue sans attendre le résultat de save().

10
yegor256

Vous pouvez utiliser Future-AsyncResult pour cela.

@Async
public Future<Page> findPage(String page) throws InterruptedException {
    System.out.println("Looking up " + page);
    Page results = restTemplate.getForObject("http://graph.facebook.com/" + page, Page.class);
    Thread.sleep(1000L);
    return new AsyncResult<Page>(results);
}

Référence: https://spring.io/guides/gs/async-method/

7
user3177227

Ce n'est probablement pas une vraie solution, mais maintenant - dans Java 8 - Vous pouvez faire en sorte que ce code paraisse au moins un peu meilleur en utilisant l'expression lambda.

final String x = "somethingelse";
new Thread(() -> {
        x.matches("something");             
    }
).start();

Et vous pouvez même le faire en une ligne, tout en restant assez lisible.

new Thread(() -> x.matches("something")).start();
3
kcpr

Vous pouvez utiliser AsyncFunc à partir de Cactoos :

boolean matches = new AsyncFunc(
  x -> x.matches("something")
).apply("The text").get();

Il sera exécuté à l'arrière-plan et le résultat sera disponible dans get() en tant que Future.

2
yegor256

Java fournit également un moyen simple d'appeler des méthodes asynchrones. dans Java.util.concurrent nous avons ExecutorService qui nous aide à faire de même. Initialisez votre objet comme ceci -

 private ExecutorService asyncExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

puis appelez la fonction like-

asyncExecutor.execute(() -> {

                        TimeUnit.SECONDS.sleep(3L);}
2
Anand

Ce n’est pas vraiment lié, mais si j’appelais de manière asynchrone une méthode, par exemple. allumettes (), je voudrais utiliser:

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>() {

        @Override
        public Boolean call() throws Exception {
            return x.matches(y);
        }

    });
}

Ensuite, pour appeler la méthode asynchrone, je voudrais utiliser:

String x = "somethingelse";
try {
    System.out.println("Matches: "+matches(x, "something").get());
} catch (InterruptedException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    e.printStackTrace();
}

J'ai testé cela et ça marche. J'ai pensé que cela pourrait aider les autres s'ils venaient pour la "méthode asynchrone".

2
FlameBlazer