Concurrency 不可变对象的真正好处是什么

Concurrency 不可变对象的真正好处是什么,concurrency,immutability,Concurrency,Immutability,我经常听到人们说,当使用多个线程时,管理不可变对象更容易,因为当一个线程访问一个不可变对象时,它不必担心另一个线程正在更改它 那么,如果我有一个公司所有员工的不变列表,并且新员工被雇佣了,会发生什么呢?在这种情况下,必须复制不可变列表,并且该列表的新副本必须包含另一个employee对象。然后,对员工名单的引用应指向新的名单 当这种情况发生时,列表本身不会改变,但是对该列表的引用会改变,因此代码“看到”不同的数据 如果是这样的话,我不明白为什么不可变对象在使用多线程时会让我们的生活更轻松。我错过

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

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

当这种情况发生时,列表本身不会改变,但是对该列表的引用会改变,因此代码“看到”不同的数据


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

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

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

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

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

这在带有垃圾收集的编程语言中可以顺利工作,因为这允许它让新的数据结构引用旧对象,只需替换已更改的对象(及其父对象),而无需担心哪些对象仍在使用,哪些没有使用。

下面是一个示例:(a)我们有一个不变的列表,(b)我们有向列表中添加元素的writer线程和(c)1000个读取线程,它们在不更改列表的情况下读取列表

不用锁也能用

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


它值钱吗?不知道。

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