Erlang Processes vs Java Threads

2022-08-31 15:06:14

我正在读Saša Jurić的“Elixir in Action”一书,在第一章中说:

Erlang 进程彼此完全隔离。它们不共享内存,一个进程的崩溃不会导致其他进程的崩溃。

Java线程不是也是如此吗?我的意思是,当Java线程崩溃时,它也不会使其他线程崩溃 - 特别是如果我们正在查看请求处理线程(让我们从此分解中排除该线程)main


答案 1

在我之后重复:“这些是不同的范式”

大声说20次左右 这是我们此刻的口头禅

如果我们真的必须比较苹果和橙子,让我们至少考虑一下“成为水果”的常见方面在哪里相交。

Java“对象”是Java程序员的基本计算单元。也就是说,对象(基本上是具有手臂和腿的结构,其封装比C++中更严格)是您用来对世界进行建模的主要工具。你认为“这个对象知道/拥有并在它上面执行,携带它所到之处,并且可以通过调用定义为其公共接口一部分的函数/方法与其他对象进行通信。它是一个名词,这个名词可以做一些事情。也就是说,你把你的思维过程围绕着这些计算单元。默认情况是对象之间发生的事情按顺序发生,并且崩溃会中断该序列。它们被称为“对象”,因此(如果我们忽略Alan Kay的原始含义),我们得到“对象导向”。Data {X,Y,Z}Functions {A(),B(),C()}Data

Erlang“进程”是Erlang程序员的基本计算单元。过程(基本上是一个在自己的时间和空间中运行的独立顺序程序)是Erlanger对世界进行建模的主要工具(1)。与Java对象定义封装级别的方式类似,Erlang进程也定义了封装级别,但在Erlang的情况下,计算单元彼此完全切断。您不能在另一个进程上调用一个方法或函数,也不能访问其中的任何数据,一个进程甚至不能在与其他任何进程相同的计时上下文中运行,并且不能保证消息接收相对于可能发送消息的其他进程的顺序。它们也可能完全位于不同的行星上(而且,想想看,这实际上是合理的)。它们可以彼此独立地崩溃,其他进程只有在故意选择受到影响时才受到影响(甚至这涉及消息传递:本质上是注册以接收来自死亡过程的遗书,这本身并不能保证以相对于整个系统的任何顺序到达, 您可能会选择或不选择对此做出反应)。

Java直接在复合算法中处理复杂性:对象如何协同工作以解决问题。它被设计为在单个执行上下文中执行此操作,Java 中的缺省情况是顺序执行。Java中的多个线程表示多个正在运行的上下文,并且是一个非常复杂的主题,因为不同计时上下文中的活动对彼此(以及整个系统:因此防御性编程,异常方案等)都有影响。在Java中说“多线程”意味着与在Erlang中不同的东西,事实上,这在Erlang中甚至从未说过,因为它始终是基本情况。请注意,Java线程意味着与时间有关的隔离,而不是内存或可见引用 - Java中的可见性是通过选择私有的和公共的来手动控制的;系统的通用可访问元素必须设计为“线程安全”和可重入,通过排队机制按顺序排列,或者采用锁定机制。简而言之:调度是线程/并发 Java 程序中手动管理的问题。

Erlang 在执行时序(调度)、内存访问和引用可见性方面分离了每个进程的运行上下文,并且通过完全隔离算法来简化算法的每个组件。这不仅仅是默认情况,这是此计算模型下唯一可用的情况。这是以永远不知道任何给定操作的顺序为代价的,一旦你的一部分处理序列越过了消息障碍 - 因为消息本质上都是网络协议,并且没有可以保证在给定上下文中执行的方法调用。这类似于为每个对象创建一个JVM实例,只允许它们跨套接字进行通信 - 这在Java中会非常麻烦,但这是Erlang设计的工作方式(顺便说一句,这也是编写“Java微服务”概念的基础,如果抛弃流行语往往带来的面向Web的包袱 - Erlang程序是, 默认情况下,为微服务群)。这一切都是关于权衡的。

这些是不同的范式。我们可以找到的最接近的共性是说,从程序员的角度来看,Erlang进程类似于Java对象。如果我们必须找到一些东西来比较Java线程...好吧,我们根本不会在Erlang中找到这样的东西,因为在Erlang中没有这种可比较的概念。打败一匹死马:这些是不同的范式。如果你在Erlang中写了一些重要的程序,这将变得显而易见。

请注意,我说的是“这些是不同的范式”,但甚至没有触及OOP与FP的话题。“用Java思考”和“用Erlang思考”之间的区别比OOP与FP更基本(事实上,人们可以为Erlang VM编写一种像Java一样的OOP语言 - 例如:Erlang中OOP对象的实现

虽然Erlang的“面向并发”或“面向过程”的基础确实更接近Alan Kay在创造术语“面向对象”(2)时的想法,但这并不是重点。Kay得到的是,可以通过将计算机切割成离散的块来降低系统的认知复杂性,而隔离是必要的。Java以一种基本上仍然是过程性的方式实现了这一点,但是围绕一种特殊的语法构建代码,而不是称为“类定义”的高阶调度闭包。Erlang 通过将每个对象的运行上下文拆分起来来实现此目的。这意味着Erlang的东西不能互相调用方法,但Java的东西可以。这意味着Erlang的东西可以孤立地崩溃,但Java的东西不能。这种基本差异产生了大量的含义 - 因此是“不同的范式”。权衡。


脚注:

  1. 顺便说一句,Erlang实现了“actor模型”的一个版本,但是我们没有使用这个术语,因为Erlang早于这个模型的普及。Joe在设计Erlang并撰写论文时并没有意识到这一点
  2. Alan Kay在创造“面向对象”一词时已经说了很多关于他的意思,最有趣的是他对消息传递(从一个具有自己的时间和内存的独立进程到另一个独立进程的单向通知)VS调用(具有共享内存的顺序执行上下文中的函数或方法调用)的看法 - 以及编程语言呈现的编程接口和下面的实现之间的界限如何模糊。

答案 2

绝对不是。Java 中的所有线程共享相同的地址空间,因此一个线程可以丢弃另一个线程拥有的内容。在Erlang VM中,这是不可能的,因为每个进程都与其他所有进程隔离。这就是他们的全部意义。每当您希望一个进程对另一个进程中的数据执行某些操作时,您的代码都必须向另一个进程发送消息。进程之间唯一共享的是大型二进制对象,这些对象是不可变的。


推荐