为什么Java没有async/await?
使用 async/await,可以以命令式样式对异步函数进行编码。这可以极大地促进异步编程。在C#中首次引入后,它被许多语言所采用,例如JavaScript,Python和Kotlin。
EA Async 是一个库,它向 Java 添加了类似 async/await 的功能。该库抽象化了使用ComppletableFutures的复杂性。
但是,为什么async/await既没有被添加到Java SE中,也没有计划在将来添加它呢?
使用 async/await,可以以命令式样式对异步函数进行编码。这可以极大地促进异步编程。在C#中首次引入后,它被许多语言所采用,例如JavaScript,Python和Kotlin。
EA Async 是一个库,它向 Java 添加了类似 async/await 的功能。该库抽象化了使用ComppletableFutures的复杂性。
但是,为什么async/await既没有被添加到Java SE中,也没有计划在将来添加它呢?
简短的回答是,Java的设计者试图消除对异步方法的需求,而不是促进它们的使用。
根据Ron Pressler的谈话,使用ComppletableFuture进行异步编程会导致三个主要问题。
虽然 async/await 解决了第一个问题,但它只能部分解决第二个问题,而根本不能解决第三个问题(例如,C# 中执行 await 的所有方法都必须标记为 async)。
但是为什么需要异步编程呢?只是为了防止线程阻塞,因为线程很昂贵。因此,在项目中,Loom Java的设计人员不是在Java中引入异步/等待,而是在虚拟线程(又名纤维/轻量级线程)上工作,这将旨在显着降低线程的成本,从而消除异步编程的需要。这将使上述所有三个问题也过时。
迟到总比不到好!!!Java在试图提出可以并行执行的更轻量级的执行单元方面晚了10多年。顺便说一句,Project loom还旨在公开Java中的“分隔延续”,我相信这只不过是C#的旧的“yield”关键字(再次晚了近20年!!)
Java确实认识到需要解决由asyn await(或者实际上是C#中的任务)解决的更大问题,这是一个伟大的想法。Async Await更像是一种语法糖。非常显着的改进,但仍然不是解决操作系统映射线程比预期重的实际问题的必要条件)。
在这里查看项目提案:https://cr.openjdk.java.net/~rpressler/loom/Loom-Proposal.html 并导航到最后一节“其他方法”。你会看到为什么Java不想引入async/await。
话虽如此,我并不真正同意所提供的推理。无论是在这个建议中,还是在斯蒂芬的回答中。
首先,让我们诊断斯蒂芬的答案
Stephan说,需要异步编程来防止阻塞(OS)线程,因为(OS)线程很昂贵,这是正确的。这正是需要虚拟线程(或C#任务)的全部原因。您应该能够在不失眠的情况下“阻止”这些任务。为了不失去睡眠,要么调用例程本身应该是一个任务,要么阻塞应该在非阻塞IO上,框架足够智能,在这种情况下不会阻塞调用线程(延续的力量)。
C#支持这一点,而提议的Java功能旨在支持这一点。根据提出的Java api,在虚拟线程上的阻塞将需要在Java中调用vThread.join()方法。它如何真正比调用 await workDoneByVThread() 更有益?
现在让我们来看看项目织机提案的推理
延续和纤程主导 async/await,因为 async/await 很容易通过延续实现(事实上,它可以使用一种称为无堆栈延续的弱分隔延续形式来实现,这些延续不会捕获整个调用堆栈,而只能捕获单个子例程的本地上下文),但反之亦然。
我不简单地理解这句话。如果有人这样做,请在评论中告诉我。
对我来说,async/await是使用延续来实现的,就堆栈跟踪而言,由于光纤/虚拟线程/任务位于虚拟机中,因此必须能够管理该方面。事实上,.net工具确实可以管理这一点。
虽然async/await使代码更简单,并使其看起来像正常的顺序代码,但与异步代码一样,它仍然需要对现有代码进行重大更改,在库中提供显式支持,并且不能与同步代码很好地互操作
我已经介绍了这一点。不对现有代码进行重大更改并且库中没有显式支持实际上意味着不能有效地使用此功能。除非Java的目标是将所有线程透明地转换为虚拟线程,否则它不能也不这样做,否则这句话对我来说没有意义。
作为一个核心思想,我发现Java虚拟线程和C#任务之间没有真正的区别。项目织机还以工作窃取调度程序为目标,作为默认值,与.Net默认使用的调度程序相同(https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskscheduler?view=net-5.0,滚动到最后一个备注部分)。似乎唯一的争论是关于应该采用什么语法来使用这些语法。
采用 C#
Java的目标是:
恕我直言,Java的选择比C#更糟糕。拥有单独的接口和类实际上非常清楚地表明行为有很大不同。当程序员没有意识到她现在正在处理不同的东西时,或者当库实现更改以利用新构造但最终阻止调用(非虚拟)线程时,保留相同的旧接口可能会导致微妙的错误。
此外,没有特殊的语言语法意味着阅读异步代码将仍然难以理解和推理(我不知道为什么Java认为程序员爱上了Java的Thread语法,他们会很高兴知道,他们将使用可爱的Thread类,而不是编写同步代码)
哎呀,甚至Javascript现在也有异步等待(具有其所有的“单线程性”)。