首页 文章

Erlang进程与Java线程

提问于
浏览
53

我正在读"Elixir in Action" book by Saša Jurić, and in the first chapter它说:

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

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

6 回答

  • 4

    Repeat after me: "These are different paradigms"

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

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

    Java "objects"是Java程序员的基本计算单元 . 也就是说, object (基本上是一个带有手臂和腿的结构,具有封装somewhat more strictly enforced than in C++)是您用来模拟世界的主要工具 . 你认为“这个对象知道/有 Data {X,Y,Z} 并在其上执行 Functions {A(),B(),C()} ,随身携带 Data ,并且可以通过调用定义为公共接口一部分的函数/方法与其他对象通信 . 它是一个名词,那个名词确实如此东西.". That is to say, you orient your thought process around these units of computation. The default case is that things that happen amongst the objects occur in sequence, and a crash interrupts that sequence. They are called " objects " and hence (if we disregard Alan Kay's original meaning) we get " object orientation“ .

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

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

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

    这些是不同的范例 . 我们可以找到的最接近的共性是说程序员's perspective, Erlang processes are analogous to Java objects. If we must find something to compare Java threads to... well, we'根本不会在Erlang中找到类似的东西,因为在Erlang中没有这样的可比概念 . 击败死马:这些是不同的范例 . 如果你在Erlang中写一些非平凡的程序,这将很明显 .

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

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


    脚注:

    • 顺便说一下,Erlang实现了一个版本“the actor model”,但我们不使用这个术语,因为Erlang早于这个模型的普及 . 当他设计Erlang并写下his thesis时,Joe并没有意识到这一点 .

    • Alan Kay已经说了很多关于what he meant when he coined the term "object oriented",最有趣的是his take关于消息传递(从一个独立进程的单向通知,有自己的时间和内存到另一个)VS调用(顺序执行上下文中的函数或方法调用)共享内存) - 以及如何在编程语言和下面的实现所呈现的编程接口之间模糊一点 .

  • 1

    为了补充以前的答案,Java线程有两种类型:守护进程和非守护进程 .

    要更改线程的类型,可以调用.setDaemon(boolean on) . 不同之处在于守护程序线程不会阻止JVM退出 . 正如线程的Javadoc所说:

    当运行的唯一线程都是守护程序线程时,Java虚拟机退出 .

    这意味着:用户线程(未专门设置为守护进程的线程)使JVM无法终止 . 另一方面,当所有非守护程序线程都完成时,守护程序线程可能正在运行,在这种情况下JVM将退出 . 所以,回答你的问题:你可以启动一个在完成后不退出JVM的线程 .

    至于与Erlang / Elixir的比较,请不要忘记: they are different paradigms, as already mentioned.

    JVM模仿Erlang的行为并非不可能,尽管它不是为了它的目的,因此,它需要进行大量的权衡 . 以下项目试图实现这一目标:

  • 15

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

  • 2

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

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

  • 16

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

    让我问一个反问:为什么你认为 Thread.stop() 已经被弃用了十多年?之所以恰恰是对上述陈述的否定 .

    举两个具体的例子:你_64792_一个线程,当它执行的东西听起来像 System.out.println()Math.random() 一样无害 . 结果:现在,整个JVM的这两个功能都已中断 . 这同样适用于您的应用程序可能执行的任何其他同步代码 .

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

    理论上可以对应用程序进行编码,使得绝对不会使用受锁保护的共享资源;但这只会有助于指出Java线程与代码相关的确切程度 . 并且"independence"实现仅适用于请求处理线程,而不适用于此类应用程序中的所有线程 .

  • 85

    对于Java线程也不是这样吗?我的意思是当Java线程崩溃时,它也不会崩溃其他线程

    是和否我解释:

    • 引用共享内存:Java进程中的不同线程共享整个堆,因此线程可以以大量计划和未计划的方式进行交互 . 堆栈中的 However 对象(例如,传递给被调用方法的上下文)或 ThreadLocal 是它们自己的线程(除非它们启动分享参考) .

    • 崩溃:如果线程在Java中崩溃( Throwable 传播到 Thread.run() ,或某些东西被循环或阻塞),那么该事故可能不会影响其他线程(例如,服务器中的连接池将继续运行) . However 因为不同的线程互动 . 如果其中一个线程异常结束(例如,一个线程试图从另一个没有关闭其末端线程的空管道中读取),则其他线程很容易被搁浅 . 因此,除非开发人员非常偏执,否则很可能会出现副作用 .

    我怀疑任何其他范式都打算将线程作为完全独立的岛屿运行 . 他们必须以某种方式分享信息和协调 . 然后就有机会搞砸了 . 只是他们会采取一种更具防御性的方法,“让你少挂绳子”(与指针相同的习语) .

相关问题