【问题标题】:What is the real benefit of Immutable Objects不可变对象的真正好处是什么
【发布时间】:2018-08-27 09:45:05
【问题描述】:

我总是听到人们说,在使用多个线程时管理不可变对象会更容易,因为当一个线程访问一个不可变对象时,不必担心另一个线程正在更改它。

好吧,如果我有一个公司所有员工的不可变列表并且雇用了一位新员工,会发生什么?在这种情况下,必须复制不可变列表,并且它的新副本必须包含另一个员工对象。然后对员工列表的引用应指向新列表。

当这种情况发生时,列表本身并没有改变,但是对这个列表的引用发生了变化,因此代码“看到”了不同的数据。

如果是这样,我不明白为什么在使用多线程时不可变对象会使我们的生活更轻松。我错过了什么?

【问题讨论】:

标签: concurrency immutability


【解决方案1】:

可变数据并发更新的主要问题是,线程可能会感知到来自不同版本的变量值,即单次更新时新旧值的混合,形成不一致的状态,违反了这些变量的不变量.

例如,参见 Java 的 ArrayList。它有一个 int 字段保存当前大小和一个数组的引用,该数组的元素是对所包含对象的引用。这些变量的值必须满足某些不变量,例如如果大小不为零,则数组引用永远不会是null,并且数组长度始终大于或等于大小。当看到这些变量的不同更新值时,这些不变量不再成立,因此线程可能会看到一个从未以这种形式存在的列表内容,或者因虚假异常而失败,报告本应不可能的非法状态(如 NullPointerExceptionArrayIndexOutOfBoundeException)。

请注意,线程安全或并发数据结构仅解决有关数据结构内部的问题,因此操作不会再因虚假异常而失败(关于集合的状态,我们尚未讨论包含元素的状态) ,但是在这些集合上迭代或以任何形式查看多个包含元素的操作仍然可能会观察到关于包含元素的不一致状态。这也适用于 check-then-act 反模式,其中应用程序首先检查条件(例如使用 contains),然后再对其进行操作(如获取、添加或删除元素),而条件可能会改变介于两者之间。

相比之下,处理不可变数据结构的线程可能会处理它的过时版本,但属于该结构的所有变量彼此一致,反映相同的版本。执行更新时,您无需考虑排除其他线程,这根本没有必要,因为其他线程看不到新的数据结构。发布新版本的整个任务简化为发布对数据结构新版本的根引用的任务。如果你不能阻止其他线程处理旧版本,最糟糕的事情是你可能不得不在之后使用新数据重复操作,换句话说,在最坏的情况下只是一个性能问题。

这与带有垃圾回收的编程语言配合得很好,因为这允许它让新数据结构引用旧对象,只需替换更改的对象(及其父对象),而无需担心哪些对象仍在使用中而不是。

【讨论】:

  • 假设我有 3 个线程添加到同一个不可变列表中。所以每个线程都有自己的列表(它们共享的只是一个初始的空列表)——现在呢?您仍然需要某种形式的同步来评估收集的数据。拥有每个线程的可变列表有什么区别?
  • @zzz777 你不能有“3 个线程添加到同一个不可变列表”。作为一个不可变的列表意味着添加它是不可能的。通过改变数据重新设计软件以使其无法工作是一个巨大的挑战,并且不一定适用于所有类型的任务。当然,对于想要操作列表的线程来说,本地的可变列表是一个很好的工具,并且不受数据竞争的影响,但这与关于共享数据结构的讨论无关,由多个线程同时访问。
  • 在我看来,可以添加到不可变列表 - 每次添加后您都会获得对新列表的引用。
  • @zzz777 您可以执行任意操作来生成新列表,包括生成比旧列表具有更多元素的新列表。正如答案中所说,关键操作是发布新列表,通常是引用的原子交换。如果比较和设置失败,则意味着您的操作基于过时的数据,因此您必须再次执行此操作。正如答案所说,“可能发生的最糟糕的事情是,您可能不得不在之后使用新数据重复操作”。读者将始终看到一致的列表。因此,这仅适用于少数更新程序。
  • 一些指向一些扩展现实生活的例子可能会有所帮助。我做了相当多的 scala 类来了解基础知识,但实际生活中的好处仍然不清楚。
【解决方案2】:

这是一个示例:(a) 我们有一个不可变的列表,(b) 我们有向列表添加元素的写入线程,以及 (c) 1000 个读取线程读取列表而不更改它。

它可以在没有锁的情况下工作。

如果我们有多个写线程,我们仍然需要一个写锁。如果我们必须从列表中删除条目,我们将需要读写锁。

它有价值吗?不知道。

【讨论】:

    猜你喜欢
    • 2010-12-11
    • 2012-04-01
    • 2010-12-15
    • 2015-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    • 1970-01-01
    相关资源
    最近更新 更多