C#async/await的Java等价物?

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

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

不,在Java中,甚至在V5之前的C#中,都没有任何与async/await等价的东西。

在后台构建状态机是一个相当复杂的语言特性。

在Java中,语言对异步/并发的支持相对较少,但java.util.concurrent包中包含了许多与此相关的有用的班级。(不完全等同于任务并行库,但与其最接近。)

Java本身没有对等的功能,但存在提供类似功能的第三方库,例如基利姆

Java本身没有任何东西可以让您像async/await关键字那样执行此操作,但如果您确实希望使用倒计时锁存器,则可以执行此操作。您,然后通过传递它来模仿async/await(至少在Java7中)。在Android单元测试中,这是一种常见的做法,我们必须进行异步调用(通常是由处理程序发布的Runnable),然后等待结果(倒计时)。

然而,在你的应用程序中使用它,而不是在你的测试中,_我所推荐的ABC_0。这将是非常粗制滥造的,因为CountDownLatch依赖于你在正确的地方有效地倒数正确的次数。

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

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

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

例如,Servlet3.0提供了异步处理。JavaFX提供了javafx.concurrent.Task。不过,它们没有优雅的语言特征。他们通过普通的回调来工作。

在语言级别上,Java中没有与C#Async/Await等价的语言。被称为纤维又名协作线程又名轻量级线程的概念可能是一个有趣的替代方案。您可以找到支持纤程的Java库。

实现纤程的Java库

您可以阅读本文(来自Quasar),了解光纤的详细介绍。它涵盖了什么是线程,如何在JVM上实现纤程,以及一些特定于Quasar的代码。

异步等待是语法糖。Async和Await的本质是状态机。编译器将把异步/等待代码转换为状态机。

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

C#中的异步版本库函数有点像Java中的异步通道概念。例如,我们有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);
}
}

当异步操作完成时,await使用延续来执行附加代码(client.GetStringAsync(...))。

因此,作为最接近的近似,我将使用基于CompletableFuture<T>(相当于.NETTask<TResult>的Java8)的解决方案来异步处理HTTP请求。

25-05-2016更新到AsyncHttpClient V.2发布于2016年4月13日:

因此,与AccessTheWebAsync()的OP示例等效的Java 8如下所示:

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日发布的异步TTPClient的版本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());
}
}

我制作并发布了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;
}

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

不幸的是,Java没有async/await的等价物。你能得到的最接近的可能是来自Guava和Listener Chaining的ListenableFuture,但对于涉及多个异步调用的情况,编写它仍然非常麻烦,因为嵌套级别会非常快地增长。

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

请注意,尽管此功能在C#中需要相当高级的编译器支持,但在Scala中,由于Scala中非常强大的宏系统,它可以作为库添加,因此甚至可以添加到较旧版本的Scala(如2.10)中。此外,Scala与Java是类兼容的,因此您可以在Scala中编写异步代码,然后从Java调用它。

还有另一个类似的项目,名为Akka DataFlowhttp://doc.akka.io/docs/akka/2.3-M1/scala/dataflow.html,它使用了不同的措辞,但在概念上非常相似,但使用定界延续而不是宏来实现(因此它甚至可以与较旧的Scala版本(如2.9)一起使用)。

正如所提到的,没有直接的等价物,但可以通过Java字节码修改创建非常接近的近似值(对于异步/等待指令和底层延续实现)。

我现在正在做一个在JavaFlow延续库上实现异步/等待的项目,请查看https://github.com/vsilaev/java-async-await

尚未创建Maven Mojo,但您可以使用提供的Java代理运行示例。下面是异步/等待代码的样子:

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()是使用Continuations等待CompletableFuture的函数,对“ return asyncResult(someValue)”的调用是完成关联的CompletableFuture/Continuation的函数

与C#一样,控制流被保留,异常处理可以以常规方式完成(类似于顺序执行代码中的try/catch)。

如果你只是想用干净的代码来模拟与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();
}
});
}
});

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

该方法还解决了async/await具有的“有色函数问题 ”。

在Golang(戈罗蒂内斯)中也可以发现类似的特征。

有一个";等价的";等待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);
}
}
为此,

我开发了一个库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);
}
}

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

enter image description here

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