2013-05-14 3 views
78

Я обычный разработчик C#, но иногда я разрабатываю приложение в java. Я ищу, есть ли какой-либо Java-эквивалент C# async/wait? Говоря простыми словами, что является эквивалентом Java,Java Equivalent C# async/ждут?

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

Я хотел бы быть «нормальным» C# разработчик ...;) – SpaceBison

+4

Почему это было бы неплохо: [колбеки наших поколений Перейти к себе] (http://tirania.org/blog/archive /2013/Aug-15.html) Мигелем де Иказой. – andrewdotn

+0

Текущее решение Java не должно касаться фактических значений с префиксом 'async', но вместо этого используйте значения' Future' или 'Observable'. –

ответ

89

Нет, нет никакого эквивалента в асинхронном/ждут в Java - или даже в C# до v5.

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

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

+0

Спасибо. Есть ли для этого какая-либо сторонняя библиотека? или любой план предстоящей java – user960567

+9

@ user960567: Нет, я хочу сказать, что это функция * language * - она ​​не может быть помещена исключительно в библиотеки. Я не верю, что эквивалент запланирован для Java 8 по крайней мере. –

+0

Извините, но для вашего комментария «даже в C# до v5», я думаю, что с Microsoft.Bcl.Async мы можем использовать async в .NET 4.0. – user960567

6

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

+1

Я не думаю, что эта библиотека имеет какое-либо отношение к тому, что async/await делает. – Natan

2

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

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

1

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

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

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

-5

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

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

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

+10

Это совсем не похоже. 'await' не блокируется до тех пор, пока' Task' не завершится, он просует оставшуюся часть метода в качестве продолжения 'Task', позволяя выполнению продолжить * асинхронно *, когда завершится' Task'. Блокировка нити радикально отличается. – Servy

10

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

Java библиотеки, реализующие волокна

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

+6

async/await in C# не является волокном. Это просто магия компилятора, которая использует продолжение в Promise (класс 'Task'), регистрируя обратный вызов. – UltimaWeapon

12

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

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

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

Так скажем, Ja поддерживают асинхронный/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); 
} 

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

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

Синтетический сахар? Есть ли у вас какие-либо идеи о том, как обрезать исключения в асинхронном коде и цитировать асинхронный код? –

+12

Занятия также являются синтаксическим сахаром. Компилятор создает все деревья и списки указателей функций, которые вы обычно пишете вручную для вас полностью автоматически. Эти функции/методы также являются синтаксическим сахаром. Они автоматически генерируют все gotos, которые вы, как правило, являетесь настоящими программистами, пишите вручную. Ассемблер также является синтаксическим сахаром. Настоящие программисты вручную записывают машинный код и вручную переносят его во все целевые архитектуры. – yeoman

+15

Мысль abou itt, сами компьютеры - всего лишь синтаксический сахар для l4m3 n00bz. Настоящие программисты припадают к крошечным интегральным схемам к деревянной доске и соединяют их с золотой проволокой, потому что печатные платы представляют собой синтаксический сахар, как массовое производство, обувь или продукты питания. – yeoman

27

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

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

ОБНОВЛЕНО на 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); 
} 

Это использование было взято из ответа на How do I get a CompletableFuture from an Async Http Client request? и который в соответствии с новый API, представленный в версии 2 от AsyncHttpClient, выпущенный на Abril 13th 2016, который уже имеет встроенную поддержку для CompletableFuture<T>.

Оригинальный ответ, используя вариант 1 из AsyncHttpClient:

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

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

  • второй немного компактнее, однако она представит новую задачу, что в конечном счете будет блокировать поток на 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()); 
    } 
} 
1

я делаю и выпустила Java асинхр/ждет библиотека. 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); 
      } 
      ); 
    } 
1

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

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

Обратите внимание: хотя эта функциональность требовала довольно продвинутой поддержки компилятора на C#, в 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).

6

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

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

Нет Maven харизмы создан, но вы можете запустить примеры с прилагаемым Java агентом. Вот как асинхронные/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 является аннотацией, что флаги метода, как асинхронно исполняемым, ждать() является функцией, которая ждет на CompletableFuture с помощью продолжений и вызова «вернуться AsyncResult (SomeValue)»является то, что дорабатывает связанный CompletableFuture/Продолжение

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

0

Если вы просто после чистого кода, который имитирует тот же эффект, что и 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(); 
       } 
      }); 
     } 
    }); 
9

Заканчивать ea-async, который делает Java байткод переписывание для имитации асинхр/ждет довольно красиво. В их readme: «Это сильно вдохновлено Async-Await на .NET CLR»

+2

Кто-нибудь использует его в производстве? –

+0

Кажется, что EA делает, я не думаю, что они будут тратить деньги на то, что не подходит для производства. – BrunoJCM

0

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

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

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

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

Кроме того, если это желательно, чтобы получить возвращаемое значение из каждых асинхронного вызова метода или кодового блока, то 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());