Java 15将发布Project Loom的第一个版本。我相信这将改变JVM。在这篇文章中,我想深入探讨一下导致我相信这一点的原因。

首先,我们需要了解核心问题。然后,我将尝试描述以前的技术如何解决它。之后,我们将看到Project Loom采取的方法。最后,我将推断后者可能对生态系统产生什么影响。

Project Loom

我们首先必须记住,很长一段时间以来,计算机只有一个内核。即使这样,还是需要同时运行多个程序:这至少要运行两个操作系统和适当的程序。

为了实现并行性的幻觉,它依赖于一个技巧。它运行一个程序,如果该程序没有在特定的时间范围内完成,它将存储其状态以供以后使用。然后,它运行下一个要运行的程序。有几种算法可用来调度下一个程序:循环调度,加权循环调度等。好处是,所有这些都可以通过操作系统线程的概念很好地从开发人员中抽象出来。该操作系统通吃繁重的护理,包括存储执行的状态。但是,线程有两个缺点:

  • 线程很重,因为它带有很多状态
  • 线程需要大量的机器资源来创建

    在所有情况下,现代OS都允许M个线程,其中M是数千个线程。现代机器是多核的,提供N个核,比M少两个数量级。拥有多个内核(无论是物理内核还是虚拟内核)并不会从根本上改变底层机制:M远高于N,并且OS负责从线程到内核的映射。

阻塞线程

面的模型在传统方案中效果很好,但在网络方案中效果不佳。假设有一个Web服务器需要响应HTTP请求。在过去不太好的时候,CGI是处理请求的一种方法。它将每个请求映射到一个流程,以便处理创建整个新流程所需的请求,并在发送响应后将其清除。

Java EE应用程序服务器极大地改善了这种情况,因为实现将线程保留在池中以供以后重用。但是,想象一下响应的生成需要时间,例如因为它需要访问数据库以读取数据。在数据库返回数据之前,线程需要等待。

这意味着线程实际上正在等待其生命周期的大部分时间。一方面,此类线程自己不使用任何CPU。另一方面,它使用其他种类的资源,尤其是内存。

同样,太多线程是操作系统的负担:操作系统必须在数量有限的CPU内核上平衡大量线程。这花费了宝贵的CPU周期,因此,操作系统正在与应用程序竞争CPU。

现在,并发请求的数量可以远远超过服务器可用的线程数量。因此,阻塞的线程浪费资源,并使服务器无响应。为了解决这个问题,可以添加更多的Web服务器来处理负载:这是水平缩放。

在大多数情况下,水平缩放就足够了。等待阻塞线程所花费的时间是浪费的,但是没有任何相关的费用...除非一个人的基础架构位于'云'中。在那种情况下,人们要为未使用的资源付费:这绝不是一个明智的主意。

单线程,反应式和Kotlin协程模型

对于开发人员来说,管理多个线程很复杂。例如,如果您一直在使用(或开发)Swing应用程序,则可能知道“灰色矩形效果”。当用户与窗口的交互(例如单击)启动长时间运行的任务时,就会发生这种情况。如果将另一个窗口移到Swing窗口上,然后再移出,Swing不会重画另一个窗口与Swing窗口相交的区域,而不会留下难看的灰色矩形。原因是长时间运行的任务是在“ 事件调度线程”上启动的,而不是在专用线程上启动的。而且这很容易避免,甚至不涉及在共享的可变状态上进行同步!

为了避免这种情况,某些堆栈完全禁止开发人员使用多个线程。例如,Node.js的API仅提供一个非阻塞事件循环线程:提交的函数采用回调的形式。请注意,这不会阻止实现使用多个线程。

该反应的方法是另一种选择,其实颇为相似。尽管它摆脱了单线程API的限制,并提供了反压力机制,但它仍然需要非阻塞代码。由于OS线程很昂贵,因此Reactive将它们池化,并在整个应用程序生命周期中重复使用它们。核心过程是从池中获取空闲线程,让其执行代码,然后将线程释放回池中。这就是为什么它需要非阻塞代码的原因:如果代码阻塞了,那么执行线程将不会被释放,并且池将在某一点或另一点耗尽。

我感兴趣地观察了Reactive模型如何像篝火一样在Spring生态系统中传播,尽管我选择站在一边。恕我直言,反应式有几个缺点:

  • 编写(和阅读!)反应式代码的思维方式与编写传统代码的思维方式非常不同。我愿意承认改变心态只需要时间,持续时间取决于每个开发人员。
  • 尽管真正的开发人员不会调试,但我知道很多人会调试-包括我自己。由于上述线程切换的魔力,要跟踪一段代码及其相关的状态并不容易。这需要足够的工具,例如带有相关插件的 IntelliJ IDEA 。
  • 最后,出于相同的原因,传统的堆栈跟踪也无济于事。一些黑魔法可以绕开它。但是,这不是为了胆小者。有关选项的完整列表,请查看此文档。

Kotlin语言提供了Reactive方法的替代方法:协程。简而言之,当使用suspend关键字时,Kotlin编译器会在字节码中生成一个有限状态机。好处是在协程块中调用的函数看起来像是顺序执行的,尽管它们是并行执行的-更确切地说,取决于确切的范围,有可能会执行。

Project Loom和虚拟线程

Reactive模型和Kotlin协程都在客户端代码和JVM线程之间添加了一个额外的抽象层。框架/库的职责是动态地将一个映射到另一个。问题的症结在于JVM线程是OS线程的薄包装:请记住,OS线程创建起来很昂贵,并且数量限制为数千个。

Project Loom的目标是实际上将JVM线程与OS线程解耦

当我第一次意识到该倡议时,其想法是创建一个称为Fiber(线程,Project Loom,您能抓住麻烦吗?)的抽象。一个Fiber责任是让一个操作系统线程,使其运行代码,释放回池,就像无栈一样。

当前的建议有很大的不同:Fiber它没有使用新的类,而是重新使用了Java开发人员非常熟悉的一个类- java.lang.Thread!

因此,在新的JVM版本中,某些Thread对象可能是重量级的并映射到OS线程,而另一些对象可能是虚拟线程。

Project Loom发布的后续影响

主要问题是,既然JVM API提供了对OS线程的抽象,那么其他抽象(例如响应式和协程)又会变成什么样呢?我对预测不满意,但以下是Reactive /协程背后的公司可能采取的一些态度:

  • 正面态度,他们意识到自己的框架不再带来任何附加值,而只是重复。他们停止了开发工作,仅向现有客户提供维护版本。他们帮助说客户迁移到新的ThreadAPI,一些帮助可能是以付费咨询的形式。
  • 反面态度,他们在各自的框架中投入了大量的精力之后,他们决定继续进行,好像什么也没有发生。例如,Spring框架负责实际设计一个共享的Reactive API,称为Reactive Streams,没有Spring依赖项。当前有两种实现,RxJava v2和Pivotal的Project Reactor。另一方面,JetBrains宣传Kotlin的协程是并行运行代码的最简单方法。
  • 中间态度。这两个框架都将继续其生命,但是会将它们各自的基础实现更改为使用虚拟线程。

    由于沉没成本的谬误,排在第一位的可能性极小:销售和市场营销将努力保持其“竞争优势”-无论在他们眼中意味着什么。尽管有些工程师出于相同的原因希望保留现有代码,但其他一些工程师则将努力使用新的API。因此,我也不相信第二名也会发生。但是,我认为这两个工程派之间都发挥着力量,然后它们与市场营销/销售之间将在#3之间找到平衡。

结论

Project Looms将现有的Thread实现方式从OS线程的映射更改为可以表示此类线程或虚拟线程的抽象。就其本身而言,这是一个有趣的举动,它在一个平台上历来比创新更重视向后兼容性。与其他最新的Java版本相比,此功能是真正的游戏规则改变者。一般而言,开发人员应尽快开始熟悉它。打算学习Reactive和协程的开发人员可能应该退后一步,并评估他们是否应该学习新的ThreadAPI- 是否需要。

翻译原文

https://blog.frankel.ch/project-loom-reactive-coroutines/

扩展阅读

Project Loom地址: https://github.com/openjdk/loom

Project Loom现有状态: http://cr.openjdk.java.net/~rpressler/loom/loom/sol1_part1.html

Project Loom:Reactive模型和协程进行时(翻译)的更多相关文章

  1. Unity 协程运行时的监控和优化

    我是快乐的搬运工: http://gulu-dev.com/post/perf_assist/2016-12-20-unity-coroutine-optimizing#toc_0 --------- ...

  2. 闭包(closure)与协程共用时要注意的事情

    闭包是一种能够让你用非常舒服的方式来编程的小技巧,Go也支持闭包. 假设从来没有接触过闭包,想在一開始就弄懂什么是闭包(closure)是非常困难的,就像递归一样,直到你真正写过.用过它,你才干真正的 ...

  3. 终结python协程----从yield到actor模型的实现

    把应用程序的代码分为多个代码块,正常情况代码自上而下顺序执行.如果代码块A运行过程中,能够切换执行代码块B,又能够从代码块B再切换回去继续执行代码块A,这就实现了协程 我们知道线程的调度(线程上下文切 ...

  4. 图解协程调度模型-GMP模型

    现在无论是客户端.服务端或web开发都会涉及到多线程的概念.那么大家也知道,线程是操作系统能够进行运算调度的最小单位,同一个进程中的多个线程都共享这个进程的全部系统资源. 线程 三个基本概念 内核线程 ...

  5. Java协程实践指南(一)

    一. 协程产生的背景 说起协程,大多数人的第一印象可能就是GoLang,这也是Go语言非常吸引人的地方之一,它内建的并发支持.Go语言并发体系的理论是C.A.R Hoare在1978年提出的CSP(C ...

  6. 从Erlang进程看协程思想

    从Erlang进程看协程思想 多核慢慢火了以后,协程类编程也开始越来越火了.比较有代表性的有Go的goroutine.Erlang的Erlang进程.Scala的actor.windows下的fibr ...

  7. 第十天 多进程、协程(multiprocessing、greenlet、gevent、gevent.monkey、select、selector)

    1.多进程实现方式(类似于多线程) import multiprocessing import time,threading def thread_run():#定义一个线程函数 print(&quo ...

  8. 协程coroutine

    协程(coroutine)顾名思义就是“协作的例程”(co-operative routines).跟具有操作系统概念的线程不一样,协程是在用户空间利用程序语言的语法语义就能实现逻辑上类似多任务的编程 ...

  9. day-5 python协程与I/O编程深入浅出

    基于python编程语言环境,重新学习了一遍操作系统IO编程基本知识,同时也学习了什么是协程,通过实际编程,了解进程+协程的优势. 一.python协程编程实现 1.  什么是协程(以下内容来自维基百 ...

随机推荐

  1. S32K142学习记录_SDK手动导入

    这几天和一位工程师讨论ADC+PDB学到了很多,当然很多的时候都是我在听, 毕竟新手,顺便其中提出自己的疑问,讨论会让你学到很多 有空会将讨论整理出来 因为demo板还没有到,只能看着大佬的程序对着手 ...

  2. 利用Nginx设置跨域的方式

    1.服务端可控,添加响应头 2.服务端不可控.通过Nginx反向代理 3.服务端不可控.通过Nginx反向代理添加响应头 第一种方法.服务端可控时,可以在服务器端添加响应头(前端+后端解决) 浏览器地 ...

  3. 小技巧:用 GitBook 组织 Markdown 文档

    喜欢用 Markdown 写文档,那怎么把一个个 Markdown 文档组成在一起呢? 这篇文章,分享了一个用 GitBook 来组织 Markdown 文档的办法.一起了解下吧. Markdown ...

  4. 01 . 消息队列之(Kafka+ZooKeeper)

    消息队列简介 什么是消息队列? 首先,我们来看看什么是消息队列,维基百科里的解释翻译过来如下: 队列提供了一种异步通信协议,这意味着消息的发送者和接受者不需要同时与消息保持联系,发送者发送的消息会存储 ...

  5. IntelliJ IDEA连接不上数据库 (Connection to testdb@localhost failed. [08001] Could not create connection to database server. Attempted reconnect 3 times. Giving up.)

    问题提示为: 原因:MySQL数据库版本为8.0以上,需要在URL加上时区,即加上?serverTimezone=GMT 成功后为:

  6. SpringBoot 及其 基本原理、配置文件(二)

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 一.SpringBoot 的版本与启动过程 1.SpringBoot都是jar工程 ​ 2.Spring ...

  7. elasticsearch中保存时间格式

    利用logstash从文档中导入数据到es中,若未事先设定数据格式,有可能存储时间并未保存为date格式而是text格式. 时间若保存为text,则在会以字符串数组格式存储在es中,是乱序,不好查询. ...

  8. Java实现 LeetCode 355 设计推特

    355. 设计推特 设计一个简化版的推特(Twitter),可以让用户实现发送推文,关注/取消关注其他用户,能够看见关注人(包括自己)的最近十条推文.你的设计需要支持以下的几个功能: postTwee ...

  9. Java实现 LeetCode 202 快乐数

    202. 快乐数 编写一个算法来判断一个数是不是"快乐数". 一个"快乐数"定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过 ...

  10. Java实现找零问题

    1 问题描述 现需找零金额为n,则最少需要用多少面值为d1 < d2 < d3 < - < dm的硬币?(PS:假设这m种面值d1 < d2 < d3 < - ...