Project Loom:Reactive模型和协程进行时(翻译)
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模型和协程进行时(翻译)的更多相关文章
- Unity 协程运行时的监控和优化
我是快乐的搬运工: http://gulu-dev.com/post/perf_assist/2016-12-20-unity-coroutine-optimizing#toc_0 --------- ...
- 闭包(closure)与协程共用时要注意的事情
闭包是一种能够让你用非常舒服的方式来编程的小技巧,Go也支持闭包. 假设从来没有接触过闭包,想在一開始就弄懂什么是闭包(closure)是非常困难的,就像递归一样,直到你真正写过.用过它,你才干真正的 ...
- 终结python协程----从yield到actor模型的实现
把应用程序的代码分为多个代码块,正常情况代码自上而下顺序执行.如果代码块A运行过程中,能够切换执行代码块B,又能够从代码块B再切换回去继续执行代码块A,这就实现了协程 我们知道线程的调度(线程上下文切 ...
- 图解协程调度模型-GMP模型
现在无论是客户端.服务端或web开发都会涉及到多线程的概念.那么大家也知道,线程是操作系统能够进行运算调度的最小单位,同一个进程中的多个线程都共享这个进程的全部系统资源. 线程 三个基本概念 内核线程 ...
- Java协程实践指南(一)
一. 协程产生的背景 说起协程,大多数人的第一印象可能就是GoLang,这也是Go语言非常吸引人的地方之一,它内建的并发支持.Go语言并发体系的理论是C.A.R Hoare在1978年提出的CSP(C ...
- 从Erlang进程看协程思想
从Erlang进程看协程思想 多核慢慢火了以后,协程类编程也开始越来越火了.比较有代表性的有Go的goroutine.Erlang的Erlang进程.Scala的actor.windows下的fibr ...
- 第十天 多进程、协程(multiprocessing、greenlet、gevent、gevent.monkey、select、selector)
1.多进程实现方式(类似于多线程) import multiprocessing import time,threading def thread_run():#定义一个线程函数 print(&quo ...
- 协程coroutine
协程(coroutine)顾名思义就是“协作的例程”(co-operative routines).跟具有操作系统概念的线程不一样,协程是在用户空间利用程序语言的语法语义就能实现逻辑上类似多任务的编程 ...
- day-5 python协程与I/O编程深入浅出
基于python编程语言环境,重新学习了一遍操作系统IO编程基本知识,同时也学习了什么是协程,通过实际编程,了解进程+协程的优势. 一.python协程编程实现 1. 什么是协程(以下内容来自维基百 ...
随机推荐
- 战略威慑 51nod提高组试题
AC通道 题目背景 马奥雷利亚诺布恩迪亚上校发动了他的第三十二次战争,让我们祝他好运. 题目描述 马孔多附近有n个城市, 有n-1条双向道路连通这些城市.上校想通过摧毁两条公路的方式对当局予以威慑.但 ...
- static关键字的应用
static关键字的应用:使用静态的变量可以实现 "累加" 的效果 package com.aff.statics; public class TestCircle { pub ...
- nvm的安装,安装node,npm
先说说我为什么使用nvm吧 最近在搞react-native,就碰到了很多坑,其中就有node带来的坑,当你运行react-native start (这是rn启动服务器的命令)就会报一个正则的错误, ...
- 解决pip._vendor.urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='files.pythonhosted.org', port=443): Read timed out.问题
国内的其他镜像源清华大学 https://pypi.tuna.tsinghua.edu.cn/simple/阿里云 http://mirrors.aliyun.com/pypi/simple/中国科技 ...
- Vuex原理实现
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 思考问题 Vuex 只在更实例引入了,那么 ...
- Java面向对象中this关键字详解 意义+实例讲解【hot】
this关键字 >>>便于理解简单的定义 this关键字可以简单的理解为,谁调用this所在的方法,this就是谁. 类的构造函数与getter.setter方法常用到this关键字 ...
- Cpusets学习
1. cpusets 1.1 什么是cpusets cpusets基本功能是限制某一组进程只运行在某些cpu和内存节点上,举个简单例子:系统中有4个进程,4个内存节点,4个cpu.利用cpuset可以 ...
- Java实现 LeetCode 807 保持城市天际线 (暴力)
807. 保持城市天际线 在二维数组grid中,grid[i][j]代表位于某处的建筑物的高度. 我们被允许增加任何数量(不同建筑物的数量可能不同)的建筑物的高度. 高度 0 也被认为是建筑物. 最后 ...
- Java实现 LeetCode 738 单调递增的数字(暴力)
738. 单调递增的数字 给定一个非负整数 N,找出小于或等于 N 的最大的整数,同时这个整数需要满足其各个位数上的数字是单调递增. (当且仅当每个相邻位数上的数字 x 和 y 满足 x <= ...
- Java实现 LeetCode 97 交错字符串
97. 交错字符串 给定三个字符串 s1, s2, s3, 验证 s3 是否是由 s1 和 s2 交错组成的. 示例 1: 输入: s1 = "aabcc", s2 = " ...