it-swarm.com.de

Java-Äquivalent von C # async/waitit?

Ich bin ein normaler C # -Entwickler, aber gelegentlich entwickle ich Anwendungen in Java. Ich frage mich, ob es ein Java-Äquivalent von C # async/await gibt. In einfachen Worten, was ist das Java-Äquivalent von:

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

Nein, es gibt kein Äquivalent von async/await in Java - oder sogar in C # vor v5.

Es ist eine ziemlich komplexe Sprachfunktion, um hinter den Kulissen eine Zustandsmaschine aufzubauen.

Es gibt relativ wenig language Unterstützung für Asynchronie/Parallelität in Java, aber das Java.util.concurrent-Paket enthält eine Menge nützlicher Klassen . (Nicht ganz gleichwertig mit der Task Parallel Library, aber der nächsten Annäherung daran.)

112
Jon Skeet

Das await verwendet eine Fortsetzung, um zusätzlichen Code auszuführen, wenn die asynchrone Operation abgeschlossen ist (client.GetStringAsync(...)).

Als genaueste Annäherung würde ich eine CompletableFuture<T> (Java 8 entspricht .net Task<TResult>) - basierte Lösung verwenden, um die HTTP-Anforderung asynchron zu verarbeiten.

AKTUALISIERT am 25-05-2016 um AsyncHttpClient v.2 veröffentlicht am 13. April 2016:

Das Java 8 entspricht also dem OP-Beispiel von AccessTheWebAsync():

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

Diese Verwendung stammt aus der Antwort auf Wie erhalte ich eine CompletableFuture von einer Async Http-Client-Anfrage? und entspricht der neuen API in Version 2 von AsyncHttpClient am 13. April 2016, das hat bereits intrinsische Unterstützung für CompletableFuture<T>.

Ursprüngliche Antwort mit Version 1 von AsyncHttpClient:

Zu diesem Zweck haben wir zwei mögliche Ansätze:

  • die erste verwendet non-blocking IO und ich nenne es AccessTheWebAsyncNio. Da AsyncCompletionHandler jedoch eine abstrakte Klasse ist (anstelle einer funktionalen Schnittstelle), haben wir Da Lambda nicht als Argument übergeben werden kann, kommt es aufgrund der Syntax anonymer Klassen zu einer unvermeidlichen Ausführlichkeit. Diese Lösung entspricht jedoch am ehesten dem Ausführungsfluss des angegebenen C # -Beispiels .

  • das zweite ist etwas weniger ausführlich, es wird jedoch ein neues Task übergeben, das letztendlich einen Thread auf f.get() blockiert, bis die Antwort vollständig ist.

Erster Ansatz , ausführlicher, aber nicht blockierend:

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;
}

Zweiter Ansatz weniger ausführlich, aber blockiert einen Thread:

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());
    }
}
35
Miguel Gamboa

Check out ea-async was Java Bytecode umschreibt, um Async/Erwartung zu simulieren. Per Readme: "Es ist stark von Async-Await auf der .NET CLR inspiriert"

17
Hafthor

async und await sind syntaktische Zucker. Die Essenz von Asynchron und Warten ist Zustandsmaschine. Der Compiler wandelt Ihren async/await-Code in eine Zustandsmaschine um.

Damit async/await in realen Projekten wirklich praktikabel ist, müssen viele Async-E/A-Bibliotheksfunktionen bereits vorhanden sein. Für C # haben die meisten ursprünglichen synchronisierten E/A-Funktionen eine alternative Async-Version. Der Grund, warum wir diese Async-Funktionen benötigen, ist, dass in den meisten Fällen Ihr eigener Async/Erwartungscode auf eine Async-Methode der Bibliothek herunterläuft. 

Die Async-Versionsbibliotheksfunktionen in C # ähneln in gewisser Weise dem AsynchronousChannel-Konzept in Java. Beispielsweise haben wir AsynchronousFileChannel.read, das entweder eine Future zurückgeben oder einen Callback ausführen kann, nachdem die Leseoperation abgeschlossen ist. Aber es ist nicht dasselbe. Alle C # Async-Funktionen geben Aufgaben zurück (ähnlich wie Future, jedoch leistungsfähiger als Future).

Nehmen wir an, Java unterstützt async/await und wir schreiben folgenden Code:

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);
}

Dann würde ich mir vorstellen, dass der Compiler den ursprünglichen async/await-Code in etwa so umwandelt:

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;
}

Und hier ist die Implementierung für 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);
    }
}
15
caisx25

C # async/await ist vergleichbar mit einem Konzept, das als Fibers aka kooperative Threads aka leichte Threads bekannt ist. Es gibt kein Äquivalent zu C # async/await in Java auf der Sprachebene, aber Sie können Bibliotheken finden, die Glasfaser unterstützen.

Java-Bibliotheken, die Fibers implementieren

Sie können diesen Artikel (von Quasar) für eine schöne Einführung in Fasern lesen. Es behandelt, was Threads sind, wie Fasern in der JVM implementiert werden können, und enthält Quasar-spezifischen Code.

13
FabienB

Wie bereits erwähnt, gibt es kein direktes Äquivalent, aber mit Java Bytecode-Modifikationen (sowohl für async/await-ähnliche Anweisungen als auch für die zugrunde liegende Fortsetzungsimplementierung) könnte eine sehr enge Annäherung erstellt werden.

Ich arbeite gerade an einem Projekt, das async/await zusätzlich zu JavaFlow-Fortsetzung Bibliothek implementiert. Bitte überprüfen Sie https://github.com/vsilaev/Java-async-await

Es wurde noch kein Maven-Mojo erstellt, aber Sie können Beispiele mit dem mitgelieferten Java Agent ausführen. So sieht asynchroner/Warte-Code aus:

public class AsyncAwaitNioFileChannelDemo {

public static void main(final String[] argv) throws Exception {

    ...
    final AsyncAwaitNioFileChannelDemo demo = new AsyncAwaitNioFileChannelDemo();
    final CompletionStage<String> result = demo.processFile("./.project");
    System.out.println("Returned to caller " + LocalTime.now());
    ...
}


public @async CompletionStage<String> processFile(final String fileName) throws IOException {
    final Path path = Paths.get(new File(fileName).toURI());
    try (
            final AsyncFileChannel file = new AsyncFileChannel(
                path, Collections.singleton(StandardOpenOption.READ), null
            );              
            final FileLock lock = await(file.lockAll(true))
        ) {

        System.out.println("In process, shared lock: " + lock);
        final ByteBuffer buffer = ByteBuffer.allocateDirect((int)file.size());

        await( file.read(buffer, 0L) );
        System.out.println("In process, bytes read: " + buffer);
        buffer.rewind();

        final String result = processBytes(buffer);

        return asyncResult(result);

    } catch (final IOException ex) {
        ex.printStackTrace(System.out);
        throw ex;
    }
}

@async ist die Annotation, die eine Methode als asynchron ausführbar kennzeichnet. await () ist eine Funktion, die unter Verwendung von Fortsetzungen auf CompletableFuture wartet und durch einen Aufruf von "return asyncResult (someValue)" die zugehörige CompletableFuture/Continuation abschließt

Wie bei C # bleibt der Steuerungsfluss erhalten, und die Ausnahmebehandlung kann auf reguläre Weise erfolgen (try/catch wie in sequenziell ausgeführtem Code).

7
Valery Silaev

Java selbst hat keine äquivalenten Funktionen, aber es gibt Bibliotheken von Drittanbietern, die ähnliche Funktionen bieten, z. B. Kilim .

6

Es gibt kein Java-basiertes Element, mit dem Sie dies tun können, beispielsweise async/await-Schlüsselwörter. Was Sie jedoch tun können, wenn Sie wirklich wollen, ist ein CountDownLatch . Sie könnten dann async/await imitieren, indem Sie dies herumgeben (zumindest in Java7). Dies ist eine übliche Praxis beim Testen von Android-Geräten, bei der wir einen asynchronen Anruf tätigen müssen (normalerweise ein ausführbarer Aufruf eines Handlers) und dann auf das Ergebnis warten (Countdown). 

Wenn Sie dies jedoch in Ihrer Anwendung im Gegensatz zu Ihrem Test verwenden, ist NICHT, was ich empfehle. Das wäre äußerst minderwertig, da CountDownLatch davon abhängt, dass Sie effektiv die richtige Anzahl und an den richtigen Stellen herunterzählen. 

3
stevebot

Verstehe zuerst, was async/await ist. Dies ist eine Möglichkeit für eine Single-Threaded-GUI-Anwendung oder einen effizienten Server, um mehrere "Fasern" oder "Co-Routinen" oder "leichte Threads" in einem einzigen Thread auszuführen.

Wenn Sie mit normalen Threads zufrieden sind, lautet das Java-Äquivalent ExecutorService.submit und Future.get. Dies wird blockiert, bis die Aufgabe abgeschlossen ist, und das Ergebnis wird zurückgegeben. Inzwischen können andere Threads arbeiten.

Wenn Sie den Vorteil von Fasern wie Glasfaser haben möchten, benötigen Sie Unterstützung im Container (ich meine in der GUI-Ereignisschleife oder im HTTP-Request-Handler des Servers) oder schreiben Sie Ihre eigene. Servlet 3.0 bietet beispielsweise eine asynchrone Verarbeitung. JavaFX bietet javafx.concurrent.Task. Diese verfügen jedoch nicht über die Eleganz der Sprachfunktionen. Sie arbeiten durch normale Rückrufe.

3

Ich habe Java async/await-Bibliothek erstellt und veröffentlicht. https://github.com/stofu1234/kamaitachi

Diese Bibliothek benötigt keine Compiler-Erweiterung und kann in Java stapellos IO verarbeitet werden.

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

    //LikeWebApplicationTester.Java
    BlockingQueue<Integer> AccessTheWebAsync() {
       HttpClient client = new HttpClient();
       return awaiter.await(
            () -> client.GetStringAsync("http://msdn.Microsoft.com"),
            urlContents -> {
                return urlContents.length();
            });
    }
    public void doget(){
        BlockingQueue<Integer> lengthQueue=AccessTheWebAsync();
        awaiter.awaitVoid(()->lengthQueue.take(),
            length->{
                System.out.println("Length:"+length);
            }
            );
    }
2
stofu

Java hat leider kein Äquivalent von async/await. Am nächsten kommt man wahrscheinlich mit ListenableFuture von Guava und der Listener-Verkettung, aber es wäre immer noch sehr mühsam, für Fälle zu schreiben, die mehrere asynchrone Aufrufe erfordern, da die Verschachtelungsebene sehr schnell anwachsen würde.

Wenn Sie eine andere Sprache als die JVM verwenden möchten, gibt es in Scala zum Glück async/await. Dies ist ein direktes C # async/await-Äquivalent mit einer fast identischen Syntax und Semantik: https: // github .com/scala/async/

Beachten Sie, dass diese Funktionalität zwar eine recht fortgeschrittene Compiler-Unterstützung in C # benötigte, in Scala jedoch dank eines sehr leistungsfähigen Makrosystems in Scala als Bibliothek hinzugefügt werden konnte und daher auch älteren Scala-Versionen wie 2.10 hinzugefügt werden kann. Darüber hinaus ist Scala klassenkompatibel mit Java, sodass Sie den asynchronen Code in Scala schreiben und dann von Java aus aufrufen können.

Es gibt auch ein anderes ähnliches Projekt namens Akka Dataflow http://doc.akka.io/docs/akka/2.3-M1/scala/dataflow.html , das unterschiedliche Formulierungen verwendet, konzeptionell jedoch sehr ähnlich ist, jedoch mit Trennzeichen implementiert Fortsetzungen, keine Makros (so funktioniert es auch mit älteren Scala-Versionen wie 2.9).

1

AsynHelper Die Java-Bibliothek enthält eine Reihe von Dienstprogrammklassen methoden für solche asynchronen Aufrufe (und Warten) .

Wenn eine Gruppe von Methodenaufrufen oder Codeblöcken asynchron ausgeführt werden soll, enthält das Feld Es eine nützliche Hilfsmethode / - AsyncTask
. SubmitTasks wie im folgenden Ausschnitt.

AsyncTask.submitTasks(
    () -> getMethodParam1(arg1, arg2),
    () -> getMethodParam2(arg2, arg3)
    () -> getMethodParam3(arg3, arg4),
    () -> {
             //Some other code to run asynchronously
          }
    );

Wenn gewartet werden soll, bis alle asynchronen Codes abgeschlossen sind, wird die AsyncTask.submitTasksAndWait varient kann verwendet werden.

Wenn es erwünscht ist, einen Rückgabewert von jedem asynchronen Methodenaufruf oder Codeblock zu erhalten, kann AsyncSupplier . SubmitSuppliers verwendet werden, sodass das Ergebnis vom Array der Ergebnislieferanten abgerufen werden kann wird von der Methode zurückgegeben. Unten sehen Sie den Beispielausschnitt:

Supplier<Object>[] resultSuppliers = 
   AsyncSupplier.submitSuppliers(
     () -> getMethodParam1(arg1, arg2),
     () -> getMethodParam2(arg3, arg4),
     () -> getMethodParam3(arg5, arg6)
   );

Object a = resultSuppliers[0].get();
Object b = resultSuppliers[1].get();
Object c = resultSuppliers[2].get();

myBigMethod(a,b,c);

Wenn sich der Rückgabetyp der einzelnen Methoden unterscheidet, verwenden Sie die folgende Art von Snippet.

Supplier<String> aResultSupplier = AsyncSupplier.submitSupplier(() -> getMethodParam1(arg1, arg2));
Supplier<Integer> bResultSupplier = AsyncSupplier.submitSupplier(() -> getMethodParam2(arg3, arg4));
Supplier<Object> cResultSupplier = AsyncSupplier.submitSupplier(() -> getMethodParam3(arg5, arg6));

myBigMethod(aResultSupplier.get(), bResultSupplier.get(), cResultSupplier.get());

Das Ergebnis der asynchronen Methodenaufrufe/Codeblöcke kann auch an einer anderen Codestelle im selben Thread oder in einem anderen Thread als im folgenden Snippet abgerufen werden.

AsyncSupplier.submitSupplierForSingleAccess(() -> getMethodParam1(arg1, arg2), "a");
AsyncSupplier.submitSupplierForSingleAccess(() -> getMethodParam2(arg3, arg4), "b");
AsyncSupplier.submitSupplierForSingleAccess(() -> getMethodParam3(arg5, arg6), "c");


//Following can be in the same thread or a different thread
Optional<String> aResult = AsyncSupplier.waitAndGetFromSupplier(String.class, "a");
Optional<Integer> bResult = AsyncSupplier.waitAndGetFromSupplier(Integer.class, "b");
Optional<Object> cResult = AsyncSupplier.waitAndGetFromSupplier(Object.class, "c");

 myBigMethod(aResult.get(),bResult.get(),cResult.get());
0
Loganathan

Wenn Sie nach reinem Code suchen, der den gleichen Effekt wie async/await in Java simuliert, und den Thread, den er aufgerufen hat, nicht blockiert, bis er beendet ist (z. B. in einem Test), könnten Sie etwa folgenden Code verwenden:

interface Async {
    void run(Runnable handler);
}

static void await(Async async) throws InterruptedException {

    final CountDownLatch countDownLatch = new CountDownLatch(1);
    async.run(new Runnable() {

        @Override
        public void run() {
            countDownLatch.countDown();
        }
    });
    countDownLatch.await(YOUR_TIMEOUT_VALUE_IN_SECONDS, TimeUnit.SECONDS);
}

    await(new Async() {
        @Override
        public void run(final Runnable handler) {
            yourAsyncMethod(new CompletionHandler() {

                @Override
                public void completion() {
                    handler.run();
                }
            });
        }
    });
0
Pellet