Java Equivalent С# async/await?

Я обычный разработчик С#, но иногда я разрабатываю приложения на Java. Мне интересно, есть ли какой-нибудь Java-эквивалент С# async/await? Проще говоря, что такое Java-эквивалент:

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

Ответ 1

Нет, нет эквивалента async/await в Java - или даже в С# перед v5.

Это довольно сложная языковая функция для создания конечной машины за кулисами.

В Java относительно мало поддержки языка для асинхронности / concurrency, но пакет java.util.concurrent содержит много полезных классов. (Не совсем эквивалентно параллельной библиотеке задач, но ближе всего к ней.)

Ответ 2

await использует продолжение для выполнения дополнительного кода после завершения асинхронной операции (client.GetStringAsync(...)).

Таким образом, в качестве наиболее близкого приближения я бы использовал решение на основе CompletableFuture<T> (Java 8, эквивалентное .net Task<TResult>) для асинхронной обработки запроса Http.

ОБНОВЛЕНО 25-05-2016 до версии AsyncHttpClient v.2, выпущенной 13 апреля 2016 года:

Таким образом, Java 8, эквивалентный OP-примеру AccessTheWebAsync() выглядит следующим образом:

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

Это использование было взято из ответа на Как получить CompletableFuture из запроса Async Http Client? и который соответствует новому API, предоставленному во второй версии AsyncHttpClient, выпущенной 13 апреля 2016 года, которая уже имеет встроенную поддержку CompletableFuture<T>.

Исходный ответ с использованием версии 1 AsyncHttpClient:

Для этого у нас есть два возможных подхода:

  • первый использует неблокирующий ввод-вывод, и я называю это AccessTheWebAsyncNio. Тем не менее, поскольку AsyncCompletionHandler является абстрактным классом (вместо функционального интерфейса), мы не можем передавать лямбда-аргумент в качестве аргумента. Таким образом, это связано с неизбежным многословием из-за синтаксиса анонимных классов. Однако это решение является наиболее близким к потоку выполнения данного примера С#.

  • второй немного менее многословен, однако он отправит новую задачу, которая в конечном итоге заблокирует поток в f.get() до тех пор, пока ответ не будет завершен.

Первый подход, более подробный, но не блокирующий:

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

Второй подход менее многословен, но блокирует поток:

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

Ответ 3

Откажитесь от ea-async, который переписывает байт-код Java, чтобы имитировать async/wait довольно красиво. По их readme: "Это сильно вдохновлено Async-Await на .NET CLR"

Ответ 4

async и ждут - синтаксические сахара. Суть асинхронного ожидания и ожидания - это конечный автомат. Компилятор преобразует ваш код async/await в конечный автомат.

В то же время для того, чтобы async/await действительно выполнимо в реальных проектах, нам нужно иметь много функций Асинхронных I/O-библиотек уже на месте. Для С# большинство оригинальных функций синхронизированного ввода-вывода имеют альтернативную версию Async. Причина, по которой нам нужны эти функции Async, заключается в том, что в большинстве случаев ваш собственный код async/await сведен к некоторому методу Async библиотеки.

Функции библиотеки версий Async в С# вроде как концепция AsynchronousChannel в Java. Например, у нас есть AsynchronousFileChannel.read, который может либо вернуть будущее, либо выполнить обратный вызов после выполнения операции чтения. Но это не совсем то же самое. Все функции С# Async возвращают задачи (похожие на Future, но более мощные, чем Future).

Итак, скажем, Java поддерживает async/await, и мы пишем такой код:

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

Тогда я бы предположил, что компилятор преобразует исходный код async/await в нечто вроде этого:

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

И вот реализация для 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);
    }
}

Ответ 5

На уровне языка не существует эквивалента С# async/await в Java. Концепция, известная как Волокна или кооперативные потоки, или легкие потоки, может быть интересной альтернативой. Вы можете найти библиотеки Java, обеспечивающие поддержку волокон.

Библиотеки Java, реализующие Fibers

Вы можете прочитать эту статью (от Quasar) для хорошего знакомства с волокнами. Он охватывает, что такое потоки, как волокна могут быть реализованы в JVM, и содержит некоторый специфичный для Quasar код.

Ответ 6

Как уже упоминалось, прямого эквивалента нет, но очень близкое приближение может быть создано с помощью модификаций байт-кода Java (для команд async/await-like и реализации последующих продолжений).

Сейчас я работаю над проектом, реализующим async/wait поверх библиотеки продолжения JavaFlow, пожалуйста, проверьте https://github.com/vsilaev/java-async-await

Пока нет Maven mojo, но вы можете запускать примеры с предоставленным Java-агентом. Вот как выглядит код async/await:

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 - это аннотация, которая помещает метод как асинхронно исполняемый, await() - это функция, которая ждет завершения CompletingFuture с продолжением, а вызов "return asyncResult (someValue)" - это то, что завершает ассоциированный CompletableFuture/Continuation

Как и в случае с С#, поток управления сохраняется, и обработка исключений может выполняться обычным образом (try/catch, как в последовательно исполняемом коде)

Ответ 7

Сама Java не имеет эквивалентных функций, но существуют сторонние библиотеки, которые предлагают аналогичную функциональность, например Kilim.

Ответ 8

В java нет ничего родного, позволяющего делать это как ключевые слова async/await, но что вы можете сделать, если вы действительно хотите использовать CountDownLatch. Затем вы могли бы имитировать async/await, передав это (по крайней мере, в Java7). Это обычная практика тестирования модулей Android, где мы должны сделать асинхронный вызов (обычно это runnable, отправленный обработчиком), а затем ждать результата (обратный отсчет).

Используя это, однако, внутри вашего приложения, в отличие от вашего теста, НЕ, что я рекомендую. Это было бы чрезвычайно дрянной, поскольку CountDownLatch зависит от того, как вы эффективно подсчитываете правильное количество раз и в нужном месте.

Ответ 9

Во-первых, поймите, что такое async/await. Это способ однопоточного приложения GUI или эффективного сервера для запуска нескольких "волокон" или "со-подпрограмм" или "легких потоков" в одном потоке.

Если вы используете обычные потоки, то эквивалент Java - ExecutorService.submit и Future.get. Это будет блокироваться до завершения задачи и вернуть результат. Тем временем другие потоки могут работать.

Если вы хотите получить что-то вроде волокон, вам нужна поддержка в контейнере (я имею в виду в цикле событий GUI или в обработчике запросов HTTP-сервера), или напишите свой собственный. Например, Servlet 3.0 предлагает асинхронную обработку. JavaFX предлагает javafx.concurrent.Task. Тем не менее, у них нет элегантности языковых функций. Они работают через обычные обратные вызовы.

Ответ 10

Я создаю и освобождаю Java async/await library. https://github.com/stofu1234/kamaitachi

Эта библиотека не нуждается в расширении компилятора и реализует многоуровневую обработку ввода-вывода в Java.

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

Ответ 11

Java, к сожалению, не имеет эквивалента async/await. Самое близкое, что вы можете получить, вероятно, связано с ListenableFuture от Guava и цепочкой прослушивателей, но было бы очень тяжело писать для случаев, связанных с несколькими асинхронными вызовами, поскольку уровень вложенности будет очень быстро расти.

Если вам нравится использовать другой язык поверх JVM, к счастью, в Scala присутствует async/await, который является прямым эквивалентом async/await С# с почти идентичным синтаксисом и семантикой: https://github.com/scala/async/

Обратите внимание, что хотя эта функциональность требует довольно продвинутой поддержки компилятора на С#, в Scala она может быть добавлена ​​в виде библиотеки благодаря очень мощной макросистеме в Scala и поэтому может быть добавлена ​​даже в более старые версии Scala как 2.10. Кроме того, Scala совместим с классом с Java, поэтому вы можете записать асинхронный код в Scala, а затем вызвать его из Java.

Существует также другой аналогичный проект под названием Akka Dataflow http://doc.akka.io/docs/akka/2.3-M1/scala/dataflow.html, который использует разную формулировку, но концептуально очень похож, однако реализован с использованием разграниченных продолжений, а не макросами (поэтому он работает с более старыми версиями Scala, такими как 2.9).

Ответ 12

Если вы только после чистого кода, который имитирует тот же эффект, что и async/await в java, и не против блокировки потока, который он вызывается до тех пор, пока он не будет завершен, например, в тесте, вы можете использовать что-то вроде этот код:

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

Ответ 13

Библиотека Java AsynHelper включает в себя набор служебных классов/методов для таких асинхронных вызовов (и ожидания).

Если необходимо выполнить набор вызовов методов или блоков кода асинхронно, он включает полезный вспомогательный метод AsyncTask.submitTasks, как показано ниже.

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

Если необходимо дождаться завершения выполнения всех асинхронных кодов, можно использовать переменную AsyncTask.submitTasksAndWait.

Также, если необходимо получить возвращаемое значение из каждого асинхронного вызова метода или блока кода, можно использовать AsyncSupplier.submitSuppliers, чтобы затем можно было получить результат из массива поставщиков результатов, возвращенного методом. Ниже приведен образец фрагмента:

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

Если тип возвращаемого значения каждого метода отличается, используйте приведенный ниже фрагмент кода.

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

Результат асинхронных вызовов методов/блоков кода также может быть получен в другой точке кода в том же потоке или в другом потоке, как в приведенном ниже фрагменте кода.

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

Ответ 14

Вы можете использовать класс EventQueue в java для имитации асинхронных и ожидающих функций, которые вы можете увидеть в javascript или С#.

Давайте предположим, что вы находитесь в потоке A. У вас есть функция, которая является асинхронной, и поток A должен ждать до ее завершения. Выполните код, используя функцию invokeAndWait в классе EventQueue.

public void functionWhichHasAsyncCode(){
    EventQueue.invokeAndWait(new Runnable() {
        @Override
        public void run() {

            //Code which you want to execute asynchronously.

        }
    });
}

И вызов функции из любого потока. Поток приостанавливает выполнение, ждет, пока эта функция не выполнится, и затем вызывающий поток возобновляет выполнение.

Ответ 15

Java не имеет прямого эквивалента функции языка С#, называемой async/await, однако существует другой подход к проблеме, которую пытается решить async/await. Он называется проектом Loom, который обеспечит волокна (легкие потоки пользовательского режима) для облегченного параллелизма с высокой пропускной способностью. Он будет доступен в некоторых будущих версиях OpenJDK.

Этот подход также решает проблему "цветной функции", которая есть у async/await.

Подобная особенность также может быть найдена в Голанге (goroutines).

Ответ 16

Java имеет класс под названием java.util.concurrent.Future, который эквивалентен классу С# Task.

Вы можете выполнить работу над объектом java.util.concurrent.Executor. Существует много реализаций, но ForkJoinTask.fork() стоит посмотреть, если вы нажмете ограничения потоков, как только вы попробуете это.

При запуске вы получите Future. Ваш метод продолжит работу. Когда вам нужен результат из вашего Будущего, вы вызываете get(), и он будет блокироваться, пока результат не будет готов. Это похоже на использование ключевого слова await в С#.