Java等效的C#async/await?

use*_*567 127 c# java

我是一名普通的C#开发人员,但偶尔也会用Java开发应用程序.我想知道是否有任何Java等价的C#async/await?简单来说,java的等价物是什么:

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

Jon*_*eet 123

不,在Java中没有任何等效的async/await - 甚至在v5之前的C#中也没有.

在幕后构建状态机是一种相当复杂的语言功能.

Java中对异步/并发的语言支持相对较少,但该java.util.concurrent软件包包含许多有用的.(不完全等同于任务并行库,但最接近它.)

  • @ user960567:不,我的观点是它是*语言*功能 - 它不能单独放在库中.我不相信至少有Java 8的等效计划. (12认同)
  • @DtechNet:有很多JVM机器是异步的,是的......这与*实际语言功能*支持异步有很大不同.(在async/await之前,.NET中有很多异步,但是async/await使它*更容易利用它.) (11认同)
  • @ user960567:您需要区分您正在使用的C#版本和您正在使用的.NET版本.async/await是一种*语言*功能 - 它是在C#5中引入的.是的,您可以使用Microsoft.Bcl.Async来使用async/await定位.NET 4,但您仍然需要使用C#5编译器. (10认同)
  • @rozar:不,不是真的.异步有多种选择 - 但RxJava不会像C#那样改变*语言*.我没有反对Rx,但它与C#5中的异步不同. (4认同)
  • @Aarkon:我认为除非有明确的 *language* 支持,否则答案仍然是正确的。这不仅仅是使调度更简单的库问题 - C# 编译器构建状态机的整个方式在这里都很重要。 (3认同)
  • @MohamedIqzas:这些并不真正等同于 async/await。在 Java 中编写并行和/或异步代码的方法有很多,但 async/await 提供了额外的“语言级”支持,这产生了很大的不同。 (3认同)

Mig*_*boa 35

await使用一个延续异步操作完成时执行的附加代码(client.GetStringAsync(...)).

因此,作为最接近的近似,我将使用CompletableFuture<T>(基于Java 8等效于.net Task<TResult>)的解决方案来异步处理Http请求.

2016年5月25日更新至2016年4月13日发布的AsyncHttpClient v.2:

所以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);
}
Run Code Online (Sandbox Code Playgroud)

这个用法取自如何从Async Http Client请求获取CompletableFuture的答案 这是根据2016年Abril发布的AsyncHttpClient第2版​​中提供的新API ,已经具有内在的支持CompletableFuture<T>.

使用AsyncHttpClient版本1的原始答案:

为此,我们有两种可能的方法:

  • 第一个使用非阻塞IO,我称之为AccessTheWebAsyncNio.然而,因为它AsyncCompletionHandler是一个抽象类(而不是功能接口),我们不能将lambda作为参数传递.因此,由于匿名类的语法,它会导致不可避免的冗长.但是,此解决方案最接近给定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;
}
Run Code Online (Sandbox Code Playgroud)

第二种方法不那么冗长,但阻止一个线程:

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这不是延续。这个例子忽略了 async/await 的真正目的,即释放当前线程去执行其他事情,然后在响应到达后继续在当前线程上执行该方法。(这要么是 UI 线程响应所需的,要么是减少内存使用量所必需的。)此示例所做的是普通的阻塞线程同步,以及一些回调。 (6认同)
  • .... 这个示例正是 C# 在执行异步操作时更易于编写和读取的原因。这只是 Java 中的一个痛苦。 (3认同)
  • 实际上,这相当于快乐的流动。它不包括处理异常,finally 和其他。包含它们会使代码更加复杂并且更容易出错。 (2认同)

Haf*_*hor 26

查看执行Java字节码重写的ea-async来模拟async/await非常好.根据他们的自述文件:"它受到.NET CLR上Async-Await的极大启发"

  • 有人在生产中使用它吗? (8认同)
  • EA好像有,我觉得他们不会花钱买不适合制作的东西。 (3认同)
  • 花钱买东西然后决定它不适合生产是很正常的;这是唯一的学习方法。可以在生产中不设置java代理的情况下使用;这应该会降低门槛(https://github.com/electronicarts/ea-async)。 (2认同)

小智 15

asyncawait是语法糖.异步和等待的本质是状态机.编译器会将您的异步/等待代码转换为状态机.

同时,为了使async/await在实际项目中真正切实可行,我们需要有许多Async I/O库函数.对于C#,大多数原始同步I/O函数都有一个备用的Async版本.我们需要这些异步函数的原因是因为在大多数情况下,您自己的异步/等待代码将归结为某些库的异步方法.

C#中的Async版本库函数有点像Java中的AsynchronousChannel概念.例如,我们有AsynchronousFileChannel.read,它可以在读操作完成后返回Future或执行回调.但它并不完全相同.所有C#Async函数都返回Tasks(类似于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);
}
Run Code Online (Sandbox Code Playgroud)

然后我想象编译器会将原始的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;
}
Run Code Online (Sandbox Code Playgroud)

这是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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 类也是语法糖.编译器会创建所有树和函数指针列表,您通常会手动为您自动编写.这些函数/方法也是语法糖.他们自动生成你通常会成为真正的程序员的所有人,手写.汇编程序也是语法糖.真正的程序员手动编写机器代码并手动将其移植到所有目标体系结构. (30认同)
  • 想想,计算机本身只是l4m3 n00bz的语法糖.真正的程序员将微小的集成电路焊接到木板上并用金线连接,因为电路板是合成糖,就像大规模生产,鞋子或食物一样. (24认同)
  • 合成糖?您是否知道如何围绕异步代码包装异常,并围绕异步代码进行循环? (15认同)
  • 是的,语法糖可以将维护、理解和单步执行的代码量减少几个数量级,这真是太时髦了。 (3认同)

Fab*_*enB 14

C#async/await可与称为Fibers又称协作线程(也称为轻量级线程)的概念相媲美.Java语言级别没有C#async/await的等价物,但您可以找到支持光纤的库.

实现Fibers的Java库

你可以阅读这篇文章(来自Quasar),对纤维有一个很好的介绍.它涵盖了什么线程,如何在JVM上实现光纤以及具有一些Quasar特定代码.

  • C#中的async/await不是光纤.它只是编译魔术,通过注册回调在Promise(`Task`类)上使用continuation. (9认同)

Val*_*aev 7

如前所述,没有直接的等价物,但可以使用Java字节码修改创建非常接近的近似值(对于async/await-like指令和底层延续实现).

我正在开发一个在JavaFlow连续库上实现async/await的项目,请查看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;
    }
}
Run Code Online (Sandbox Code Playgroud)

@async是将方法标记为异步可执行的注释,await()是一个使用continuation等待CompletableFuture并调用"return asyncResult(someValue)"的函数,最终确定相关的CompletableFuture/Continuation

与C#一样,控制流程被保留,异常处理可以以常规方式完成(尝试/捕获,如顺序执行的代码)


Ale*_*dov 6

Java本身没有相同的功能,但存在提供类似功能的第三方库,例如Kilim.

  • 我认为这个库与async/await没有任何关系. (2认同)

Ale*_*sky 5

首先,了解什么是 async/await。它是单线程 GUI 应用程序或高效服务器在单个线程上运行多个“纤程”或“协程”或“轻量级线程”的一种方式。

如果您可以使用普通线程,那么 Java 等价物是ExecutorService.submitFuture.get。这将阻塞直到任务完成,并返回结果。同时,其他线程可以工作。

如果您想获得纤程之类的好处,您需要容器中的支持(我的意思是在 GUI 事件循环或服务器 HTTP 请求处理程序中),或者通过编写自己的。

例如,Servlet 3.0 提供异步处理。JavaFX 提供javafx.concurrent.Task. 但是,这些没有语言功能的优雅。它们通过普通回调工作。

  • 这是一篇文章引用重新启动此答案的第一段 //start quote 对于客户端应用程序,例如 Windows 应用商店、Windows 桌面和 Windows Phone 应用程序,异步的主要好处是响应能力。这些类型的应用程序主要使用异步来保持 UI 响应。对于服务器应用程序,异步的主要好处是可扩展性。https://msdn.microsoft.com/en-us/magazine/dn802603.aspx (3认同)

Mar*_*til 5

Java 没有直接等效的 C# 语言功能称为 async/await,但是 async/await 尝试解决的问题有不同的方法。它被称为Loom项目,它将为高吞吐量并发提供虚拟线程。它将在 OpenJDK 的某些未来版本中可用。

这种方法也解决了 async/await 的“有色函数问题”。

在 Golang ( goroutines ) 中也可以找到类似的功能。

  • 我很期待这一点。我正在学习 Java,之前是 Node.js 和 Go,这很痛苦。Node.js 有 async/await (彩色函数问题从来没有困扰过我),Go 有 goroutine,所以你只需在生成的数百万个廉价 goroutine 中编写阻塞代码。Java 似乎两者都没有,需要我使用 java.util.concurrent 或 RxJava 之类的东西来使我的代码非阻塞。如果我对 Loom 项目的理解是正确的,那么它将使用 Java 编写异步代码变得像 Go 一样简单。您只需编写阻塞代码,而不必担心耗尽能够执行的“线程”。 (2认同)

小智 5

EA 开发了一个“等效的”await: https: //github.com/Electronicarts/ea-async。参考Java示例代码:

import static com.ea.async.Async.await;
import static java.util.concurrent.CompletableFuture.completedFuture;

public class Store
{
    public CompletableFuture<Boolean> buyItem(String itemTypeId, int cost)
    {
        if(!await(bank.decrement(cost))) {
            return completedFuture(false);
        }
        await(inventory.giveItem(itemTypeId));
        return completedFuture(true);
    }
}
Run Code Online (Sandbox Code Playgroud)