179

我是一名普通的 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;
}
4

16 回答 16

169

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

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

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

于 2013-05-14T09:17:32.697 回答
50

异步操作完成时使用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);
}

此用法取自如何从异步 Http 客户端请求中获取 CompletableFuture 的答案?根据 2016 年 4 月 13 日发布的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;
}

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

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());
    }
}
于 2015-02-09T18:22:44.003 回答
36

查看ea-async,它会重写 Java 字节码以很好地模拟 async/await。根据他们的自述:“它深受 .NET CLR 上的 Async-Await 的启发”

于 2016-08-16T16:29:44.307 回答
16

asyncawait是语法糖。async 和 await 的本质是状态机。编译器会将您的 async/await 代码转换为状态机。

同时,为了让 async/await 在实际项目中真正可行,我们需要大量的Async I/O 库函数已经到位。对于 C#,大多数原始同步 I/O 函数都有一个替代的异步版本。我们需要这些 Async 函数的原因是因为在大多数情况下,您自己的 async/await 代码将归结为一些库 Async 方法。

C# 中的 Async 版本库函数有点像 Java 中的 AsynchronousChannel 概念。例如,我们有 AsynchronousFileChannel.read ,它可以在读取操作完成后返回 Future 或执行回调。但它并不完全相同。所有 C# 异步函数都返回任务(类似于 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);
    }
}
于 2015-01-16T05:51:01.327 回答
15

在语言级别上,Java 中没有 C# async/await 等价物。称为Fibers aka协作线程aka轻量级线程的概念可能是一个有趣的替代方案。您可以找到为纤程提供支持的 Java 库。

实现 Fibers 的 Java 库

您可以阅读这篇文章(来自 Quasar),了解有关纤维的精彩介绍。它涵盖了线程是什么,如何在 JVM 上实现纤程,并且有一些 Quasar 特定的代码。

于 2014-09-02T13:43:07.823 回答
8

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

我现在正在开发一个在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;
    }
}

@async 是将方法标记为异步可执行的注释,await() 是使用延续等待 CompletableFuture 的函数,调用“return asyncResult(someValue)”是最终确定关联的 CompletableFuture/Continuation

与 C# 一样,保留了控制流,并且可以以常规方式进行异常处理(尝试/捕获,就像在顺序执行的代码中一样)

于 2015-11-26T12:29:29.263 回答
7

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

于 2013-05-14T10:01:35.707 回答
6

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

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

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

于 2019-09-27T14:34:37.247 回答
4

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

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

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

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

于 2014-07-04T06:37:35.240 回答
3

java 没有任何原生的东西可以让你像 async/await 关键字那样做,但是如果你真的想要的话,你可以做的是使用CountDownLatch。然后,您可以通过传递它来模仿 async/await(至少在 Java7 中)。这是 Android 单元测试中的常见做法,我们必须进行异步调用(通常是由处理程序发布的可运行对象),然后等待结果(倒计时)。

但是,我不建议您的应用程序中使用它而不是在您的测试中使用它。那将是非常粗制滥造的,因为 CountDownLatch 取决于您在正确的位置有效地倒数正确的次数。

于 2014-05-29T14:38:48.263 回答
3

我制作并发布了 Java async/await 库。 https://github.com/stofu1234/kamaitachi

该库不需要编译器扩展,在Java中实现无栈IO处理。

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

   ↓</p>

    //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);
            }
            );
    }
于 2015-03-01T15:07:41.790 回答
1

不幸的是,Java 没有等效的 async/await。您可以获得的最接近的可能是来自 Guava 的 ListenableFuture 和侦听器链接,但是对于涉及多个异步调用的情况编写仍然非常麻烦,因为嵌套级别会很快增长。

如果您可以在 JVM 之上使用不同的语言,幸运的是 Scala 中有 async/await,它是直接的 C# async/await 等价物,具有几乎相同的语法和语义: https ://github.com/scala/异步/

请注意,尽管此功能需要 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)。

于 2015-09-11T17:33:53.807 回答
1

AsynHelper Java 库包含一组用于此类异步调用(和等待)的实用程序类/方法。

如果希望异步运行一组方法调用或代码块,它包括一个有用的辅助方法AsyncTask .submitTasks,如下面的代码片段所示。

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

如果希望等到所有异步代码完成运行,可以使用AsyncTask.submitTasksAndWait变体。

此外,如果希望从每个异步方法调用或代码块中获取返回值,则可以使用AsyncSupplier .submitSupplier以便可以从方法返回的结果供应商数组中获取结果。以下是示例片段:

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());
于 2018-01-25T07:21:13.733 回答
0

如果您只是在使用干净的代码来模拟与 java 中的 async/await 相同的效果,并且不介意在完成之前阻塞它被调用的线程,例如在测试中,您可以使用类似以下代码的内容:

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();
                }
            });
        }
    });
于 2016-04-20T03:42:16.667 回答
0

EA 开发了一个“等价的”等待: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);
    }
}
于 2021-03-08T05:18:27.280 回答
0

我开发了一个库JAsync来执行此操作。它今天刚刚发布。它使开发人员的异步编程体验尽可能接近通常的同步编程,包括代码风格和调试。这是示例。

@RestController
@RequestMapping("/employees")
public class MyRestController {
    @Inject
    private EmployeeRepository employeeRepository;
    @Inject
    private SalaryRepository salaryRepository;

    // The standard JAsync async method must be annotated with the Async annotation, and return a Promise object.
    @Async()
    private Promise<Double> _getEmployeeTotalSalaryByDepartment(String department) {
        double money = 0.0;
        // A Mono object can be transformed to the Promise object. So we get a Mono object first.
        Mono<List<Employee>> empsMono = employeeRepository.findEmployeeByDepartment(department);
        // Transformed the Mono object to the Promise object.
        Promise<List<Employee>> empsPromise = JAsync.from(empsMono);
        // Use await just like es and c# to get the value of the Promise without blocking the current thread.
        for (Employee employee : empsPromise.await()) {
            // The method findSalaryByEmployee also return a Mono object. We transform it to the Promise just like above. And then await to get the result.
            Salary salary = JAsync.from(salaryRepository.findSalaryByEmployee(employee.id)).await();
            money += salary.total;
        }
        // The async method must return a Promise object, so we use just method to wrap the result to a Promise.
        return JAsync.just(money);
    }

    // This is a normal webflux method.
    @GetMapping("/{department}/salary")
    public Mono<Double> getEmployeeTotalSalaryByDepartment(@PathVariable String department) { 
        // Use unwrap method to transform the Promise object back to the Mono object.
        return _getEmployeeTotalSalaryByDepartment(department).unwrap(Mono.class);
    }
}

在调试模式下,您可以像同步代码一样看到所有变量。

在此处输入图像描述

这个项目的另一个好处是它是少数几个目前仍然活跃的同类项目之一。刚出,很有潜力

于 2021-09-13T17:42:13.357 回答