当前位置: 首页 > 面试题库 >

在没有JVM支持的情况下,如何在JVM lang中实现协程?

劳亦
2023-03-14
问题内容

阅读Loom提案后提出了这个问题,该提案描述了用Java编程语言实现协程的方法。

特别是,该提案表示,要以该语言实现此功能,将需要其他JVM支持。

据我了解,JVM上已经有多种语言将协程作为其功能集的一部分,例如Kotlin和Scala。

那么,如何在没有其他支持的情况下实现此功能,而没有其他支持,是否可以有效地实现它呢?


问题答案:

tl; dr 摘要:

特别是,该建议说,要以该语言实现此功能,将需要附加的JVM支持。

当他们说“必需”时,它们的意思是“必须以在语言之间既高效又可互操作的方式实现”。

因此,如何在没有其他支持的情况下实现此功能

有很多方法,最容易理解它如何工作(但不一定最容易实现)的方法是,在JVM之上使用自己的语义来实现自己的VM。(请注意,这 并不是
实际完成的方式,这只是关于 为什么 可以执行的一种直觉。)

没有它,能否有效实施?

并不是的。

稍长的解释

注意,Project Loom的一个目标是 纯粹 将这种抽象作为一个库来引入。这具有三个优点:

  • 引入新库比更改Java编程语言要容易得多。
  • 可以在JVM上以每种单一语言编写的程序立即使用库,而Java语言功能只能由Java程序使用。
  • 可以实现具有不使用新的JVM功能的相同API的库,这将允许您编写具有在旧JVM上运行的代码,并且可以进行简单的重新编译(尽管性能较低)。

但是,将其实现为库可避免巧妙的编译器技巧,因为 它们不涉及编译器
,因此可以将协例程转换为其他东西。没有聪明的编译器技巧,要获得良好的性能要困难得多,因此,这是对JVM支持的“要求”。

更长的解释

通常,所有通常的“强大”控制结构在计算意义上都是等效的,并且可以彼此实现。

在那些“强大的”通用控制流结构中,最著名的是尊贵的GOTO,另一种是Continuations。然后是线程和协程,这是人们不常想到的,但这也等同于GOTO:异常。

另一种可能性是重新定义调用堆栈,以便程序员可以将调用堆栈作为对象进行访问,并且可以对其进行修改和重写。(例如,许多Smalltalk方言都这样做,这也有点像在C和汇编语言中那样。)

只要拥有 其中 之一,就可以通过在另一个之上实现而拥有 所有 这些。

JVM有两个:Exceptions和GOTO,但是GOTOJVM中的 并不是 通用的,它非常有限:它仅
单个方法中起作用。(它实际上仅用于循环。)因此,这给我们留下了异常。

因此,这是对您的问题的一种可能的答案:您可以在Exceptions之上实现协同例程。

另一种可能性是根本不使用JVM的控制 而是实现自己的堆栈。

但是,这通常不是在JVM上实现协同例程时实际采用的路径。最有可能实现共同例程的人会选择使用蹦床并将部分执行上下文重新定义为对象。就是说,例如,如何在CLI的C♯中实现Generators(不是JVM,但是挑战是相似的)。通过将方法的局部变量提升到上下文对象的字段中,并在每个yield语句的该对象上将方法拆分为多个方法,然后将它们转换为状态,可以实现C♯中的生成器(基本上是受限的半协例程)。机器,并仔细地通过上下文对象上的字段线程化所有状态更改。和之前async/await
作为一种语言功能出现了,一个聪明的程序员也使用相同的机器实现了异步编程。

但是 ,这就是您最可能提到的文章:所有这些机器都是昂贵的。如果您实现自己的堆栈或将执行上下文提升到一个单独的对象中,或者将所有方法编译为一个
巨型 方法并GOTO在任何地方使用(由于方法的大小限制,这甚至是不可能的),或者使用Exceptions作为控件-流程,至少这两项是正确的:

  • 您的调用约定与其他语言期望的JVM堆栈布局不兼容,即失去了 互操作性
  • JIT编译器不知道什么是地狱你的代码是干什么的,并呈现字节码图案,执行流程模式和使用模式(如抛出和捕获 极大的相 量例外),它不指望,不知道如何优化,即失去 性能

Rich Hickey(Clojure的设计师)曾经在一次演讲中说:“尾巴通话,表演,互操作。选择两个。” 我将其概括为我称为 Hickey的Maxim
:“高级控制流,性能,互操作。选择两个。”

事实上,这通常很难实现,甚至 一个 互操作或性能。

而且,您的编译器将变得更加复杂。

当该构造在JVM中本地可用时,所有这些都消失了。想象一下,例如,如果JVM没有线程。然后,每种语言实现都将创建自己的线程库,该库很难,复杂,缓慢且不会与任何
其他 语言实现的线程库互操作。

最近的一个真实示例是lambda:JVM上的许多语言实现都有lambda,例如Scala。然后,Java也添加了lambda,但是由于JVM不支持lambda,因此必须对其进行
编码 ,并且Oracle选择的编码与Scala之前选择的编码不同,这意味着您无法传递Java
lambda。期望Scala的Scala方法Function。在这种情况下,解决方案是Scala开发人员完全重写了他们的lambda编码,以与Oracle选择的编码兼容。实际上,这在某些地方破坏了向后兼容性。



 类似资料:
  • 我正在尝试设置SpringXML配置,而不必创建进一步的。但是我经常遇到以下异常,即使我在 spring.xml: 我错过了什么?

  • 问题内容: 我从 JavaEE 6中 了解到,它是可选的。 因此,如果没有 web.xml ,如何告诉应用程序服务器使用Jersey作为JAX-RS规范的实现? 问题答案: 就如何在没有web.xml的情况下实现应用程序配置而言,@AlexNevidomsky的回答是正确的。您在子类上使用注释。 有关部署选项的更多信息,请参见JAX-RS规范-> 2.3发布-> 2.3.2Servlet。 或更常

  • 问题内容: 我正在尝试设置spring xml配置,而不必创建进一步的。但是,即使我将数据库属性包括在 spring.xml: 我在这里想念什么? 问题答案: 在entityManagerFactory bean定义中指定“ packagesToScan”和“ persistenceUnitName”属性。 请注意,这适用于Spring版本> 3.1

  • 问题内容: 我正在学习使用Selenium(v2.20)来领先一些 即将使用它创建浏览器测试的程序员。我想在 陷阱到达之前发现它们,而我却跌入了一个陷阱。 当我创建ChromeDriver时,它始终会弹出“ Google Chrome EULA”并 显示两个按钮:“接受并运行”和“取消”。因为我希望这是一个 自动化测试,所以让用户单击按钮是不可能的。 我查看了Chromium CommandSwi

  • 我正在尝试构建一个基本的REST服务,它使用Spring Security和OAuth2.0身份验证和授权进行安全保护。 我试图限制所涉及的元素,所以我不是复制粘贴依赖于Spring bean、Spring MVC等的Spring Security Oath XML配置,而是直接使用Spring Security Oauth类。 尝试从/oauth/Token获取访问令牌时遇到了一个障碍。我可能缺

  • 我已经安装了Android SDK最新版本和Eclipse。但我也想试试Android Studio。 我看过这个和这个帖子,但是那些解决方案改变了Android Studio(一旦下载并安装)使用的SDK实例。我想要的不是下载另一个SDK,当我已经在我的机器上安装了它。