Erlang进程与Java线程

Wan*_*ker 60 java erlang elixir

我正在阅读SašaJurić的"Elixir in Action"一书,在第一章中它说:

Erlang进程完全相互隔离.它们不共享内存,一个进程崩溃不会导致其他进程崩溃.

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

zxq*_*xq9 94

在我之后重复: "这些是不同的范例"

大声说出20次左右 - 这是我们目前的口头禅.

如果我们真的必须比较苹果和橘子,那么我们至少要考虑"成果"的共同方面在哪里相交.

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

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

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

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

这些是不同的范例.我们可以找到的最接近的共性是,从程序员的角度来看,Erlang进程类似于Java对象.如果我们必须找到一些东西来比较Java线程......好吧,我们根本不会在Erlang中找到类似的东西,因为在Erlang中没有这样的可比概念.击败死马:这些是不同的范例.如果你在Erlang中编写一些非平凡的程序,这将很明显.

请注意,我说"这些是不同的范例",但甚至没有涉及OOP与FP的主题."在Java中思考"和"在Erlang中思考"之间的区别比OOP与FP更为重要.

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


脚注:

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

  • @WandMaker很高兴听到!奇怪的是,你对一种范式的思考越多,你对别人的理解就越多.如果你有时间(提示:创造需要),这实际上是非常有趣的(对我来说).试试Prolog!试试Forth!在Guile/Scheme /中构建OOP!然后重新访问汇编程序!这个东西在你走得更深的时候真是太有趣了 - 直到它一起消失,你开始讨厌每一种语言,这就是我现在所处的位置.但问题解决仍然很有趣!^.^ (9认同)
  • 不错的文章,所以我说了21次。 (2认同)

Dan*_*iel 16

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


Pat*_*ity 14

Java进程实际上可以共享内存.例如,您可以将同一个实例传递给两个单独的线程,并且两者都可以操纵其状态,从而导致潜在的问题,例如死锁.

另一方面,Elixir/Erlang通过不变性的概念来解决这个问题,所以当你将某些东西传递给一个过程时,它将是原始值的副本.


Mar*_*nik 5

当Java线程死亡时,它也不会影响其他线程

让我提出一个反问:您为什么认为Thread.stop()已弃用了十多年?原因恰恰是上述声明的否定。

举两个具体的例子:stop()一个线程正在执行诸如System.out.println()或听起来无害的操作Math.random()。结果:这两个功能现在对于整个JVM都是无效的。这同样适用于您的应用程序可能执行的任何其他同步代码。

如果我们正在查看请求处理线程

该应用程序可以在理论上被编码,使得由锁保护绝对没有共享资源被使用过; 但是,这仅有助于指出Java线程相互依赖的确切程度。并且实现的“独立性”将仅与请求处理线程有关,而不与此类应用程序中的所有线程有关。