222 votes

L'équivalent Java de C# async/await ?

Je suis un développeur C# normal mais il m'arrive de développer des applications en Java. Je me demande s'il existe un équivalent Java de C# async/await ? En termes simples, quel est l'équivalent Java de :

async Task<int> AccessTheWebAsync()
{ 
    HttpClient client = new HttpClient();
    var urlContents = await client.GetStringAsync("http://msdn.microsoft.com");
    return urlContents.Length;
}

184voto

Jon Skeet Points 692016

Non, il n'y a pas d'équivalent d'async/await en Java - ou même en C# avant la v5.

Il s'agit d'une caractéristique assez complexe du langage pour construire une machine à états dans les coulisses.

Il y a relativement peu langue le support de l'asynchronie/de la concurence en Java, mais les java.util.concurrent contient de nombreux éléments utiles classes autour de ça. (Ce n'est pas tout à fait l'équivalent de la bibliothèque parallèle des tâches, mais c'en est l'approximation la plus proche).

52voto

Miguel Gamboa Points 808

El await utilise une continuation pour exécuter du code supplémentaire lorsque l'opération asynchrone est terminée ( client.GetStringAsync(...) ).

Donc, comme approximation la plus proche, j'utiliserais un CompletableFuture<T> (l'équivalent Java 8 de .net Task<TResult> ) pour traiter la demande Http de manière asynchrone.

MISE À JOUR le 25-05-2016 à AsyncHttpClient v.2 sortie le 13 avril 2016 :

Ainsi, l'équivalent Java 8 de l'exemple OP de AccessTheWebAsync() est le suivant :

CompletableFuture<Integer> AccessTheWebAsync()
{
    AsyncHttpClient asyncHttpClient = new DefaultAsyncHttpClient();
    return asyncHttpClient
       .prepareGet("http://msdn.microsoft.com")
       .execute()
       .toCompletableFuture()
       .thenApply(Response::getResponseBody)
       .thenApply(String::length);
}

Cet usage est tiré de la réponse à la question Comment obtenir un CompletableFuture à partir d'une requête client Http asynchrone ? et qui est conforme à la nouvelle API fournie dans la version 2 de AsyncHttpClient publié le 13 avril 2016, qui a déjà un support intrinsèque pour CompletableFuture<T> .

Réponse originale utilisant la version 1 de AsyncHttpClient :

À cette fin, nous avons deux approches possibles :

  • le premier utilise des entrées-sorties non bloquantes et je l'appelle AccessTheWebAsyncNio . Pourtant, parce que le AsyncCompletionHandler est une classe abstraite (au lieu d'une interface fonctionnelle) nous ne pouvons pas passer un lambda comme argument. Cela entraîne donc une inévitable verbosité due à la syntaxe des classes anonymes. Cependant, Cette solution est la plus proche du flux d'exécution de l'exemple C# donné. .

  • la seconde est un peu moins verbeuse, mais elle soumet un nouveau fichier Tâche qui, en fin de compte, bloquera un fil de discussion sur f.get() jusqu'à ce que la réponse soit complète.

Première approche plus verbeux mais non bloquant :

static CompletableFuture<Integer> AccessTheWebAsyncNio(){
    final AsyncHttpClient asyncHttpClient = new AsyncHttpClient();
    final CompletableFuture<Integer> promise = new CompletableFuture<>();
    asyncHttpClient
        .prepareGet("https://msdn.microsoft.com")
        .execute(new AsyncCompletionHandler<Response>(){
            @Override
            public Response onCompleted(Response resp) throws Exception {
                promise.complete(resp.getResponseBody().length());
                return resp;
            }
        });
    return promise;
}

Deuxième approche moins verbeux mais bloquant un fil :

static CompletableFuture<Integer> AccessTheWebAsync(){
    try(AsyncHttpClient asyncHttpClient = new AsyncHttpClient()){
        Future<Response> f = asyncHttpClient
            .prepareGet("https://msdn.microsoft.com")
            .execute();
        return CompletableFuture.supplyAsync(
            () -> return f.join().getResponseBody().length());
    }
}

36voto

Hafthor Points 5663

Vérifiez ea-async qui réécrit le bytecode Java pour simuler l'asynchronisme et l'attente. Selon leur readme : "Il est fortement inspiré par Async-Await sur le .NET CLR".

16voto

FabienB Points 388

Il n'y a pas d'équivalent de C# async/await en Java au niveau du langage. Un concept connu sous le nom de Fibres alias fils de coopération alias fils légers pourrait être une alternative intéressante. Vous pouvez trouver des bibliothèques Java offrant un support pour les fibres.

Bibliothèques Java implémentant les fibres

Vous pouvez lire cet article (de Quasar) pour une bonne introduction aux fibres. Il couvre ce que sont les threads, comment les fibres peuvent être implémentées sur la JVM et contient du code spécifique à Quasar.

15voto

caisx25 Points 367

asynchrone y attendre sont des sucres syntaxiques. L'essence de async et await est la machine à états. Le compilateur transformera votre code async/await en une machine à états.

En même temps, pour qu'async/await soit réellement applicable dans des projets réels, nous devons avoir beaucoup de Fonctions de la bibliothèque d'E/S asynchrones déjà en place. En C#, la plupart des fonctions d'E/S synchronisées originales ont une version alternative Async. La raison pour laquelle nous avons besoin de ces fonctions Async est que, dans la plupart des cas, votre propre code async/await se résumera à une méthode Async de la bibliothèque.

La version asynchrone des fonctions de la bibliothèque en C# est un peu comme le concept de canal asynchrone en Java. Par exemple, nous avons AsynchronousFileChannel.read qui peut soit retourner un Future, soit exécuter une callback une fois l'opération de lecture terminée. Mais ce n'est pas exactement la même chose. Toutes les fonctions asynchrones C# renvoient des Tasks (similaires à Future mais plus puissants que Future).

Supposons que Java supporte l'asynchronisme et l'attente, et que nous écrivions un code comme celui-ci :

public static async Future<Byte> readFirstByteAsync(String filePath) {
    Path path = Paths.get(filePath);
    AsynchronousFileChannel channel = AsynchronousFileChannel.open(path);

    ByteBuffer buffer = ByteBuffer.allocate(100_000);
    await channel.read(buffer, 0, buffer, this);
    return buffer.get(0);
}

J'imagine alors que le compilateur transformera le code original async/await en quelque chose comme ceci :

public static Future<Byte> readFirstByteAsync(String filePath) {

    CompletableFuture<Byte> result = new CompletableFuture<Byte>();

    AsyncHandler ah = new AsyncHandler(result, filePath);

    ah.completed(null, null);

    return result;
}

Et voici l'implémentation pour AsyncHandler :

class AsyncHandler implements CompletionHandler<Integer, ByteBuffer>
{
    CompletableFuture<Byte> future;
    int state;
    String filePath;

    public AsyncHandler(CompletableFuture<Byte> future, String filePath)
    {
        this.future = future;
        this.state = 0;
        this.filePath = filePath;
    }

    @Override
    public void completed(Integer arg0, ByteBuffer arg1) {
        try {
            if (state == 0) {
                state = 1;
                Path path = Paths.get(filePath);
                AsynchronousFileChannel channel = AsynchronousFileChannel.open(path);

                ByteBuffer buffer = ByteBuffer.allocate(100_000);
                channel.read(buffer, 0, buffer, this);
                return;
            } else {
                Byte ret = arg1.get(0);
                future.complete(ret);
            }

        } catch (Exception e) {
            future.completeExceptionally(e);
        }
    }

    @Override
    public void failed(Throwable arg0, ByteBuffer arg1) {
        future.completeExceptionally(arg0);
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X