4

我目前正在学习 java 中的线程,从我所读到的内容来看,实现它们并不是问题,而是让你的代码线程安全。这让我想到了我的问题:我是否应该尽可能使用不可变对象来确保防止并发错误?

我读过关于何时使用可变对象的不同意见,包括:

如果不可变对象是好的,为什么人们还要继续创建可变对象?(程序员.SE)

对于大型和/或复杂对象,为每次更改创建对象的新副本可能非常昂贵和/或乏味。对于具有不同身份的对象,更改现有对象比创建新的修改副本要简单和直观得多。

不可变对象(Java 教程)

对象创建的影响通常被高估,并且可以被与不可变对象相关的一些效率所抵消。这些包括由于垃圾收集而减少的开销,以及消除保护可变对象免受损坏所需的代码。

那么,在实现线程时是否有最佳实践?我是否应该尽可能尝试使用不可变对象?

4

4 回答 4

3

使用真正不可变的对象(不包含对可变类的引用的字段)是线程安全的,在实际情况下使用它们是一个好习惯,但不可变性是否可行取决于具体情况;例如,某些工具包需要 setter 或字段注入,因此不能与不可变对象一起使用。不变性通常是值对象模式中最实用的,其中对象没有任何特别复杂的行为,并且基本上封装了一个清晰可描述的值(String原始包装器,番石榴的Immutable*集合)。

通常,我会尝试使具有简单字段的对象在可行的情况下不可变,并且我发现从不可变对象最安全的默认设置开始并且仅在有特定原因需要它们时才使用突变器是很有帮助的。

类似地,如果您要依赖类的不变性来实现线程安全,通常应该声明该类final以避免各种令人头疼的问题。我并没有坚持所有类都必须是abstractor final,但可final实例化的类是一个很好的默认值。

于 2013-10-09T16:44:50.583 回答
1

是的,在多线程时,不可变对象通常是你的朋友。

有几个大优点:

  • 线程安全。如果你不能改变它的状态,那么两个线程就不可能同时更新它或看到它的不同版本。

  • 没有锁定。因为没有任何变化,所以没有必要使用synchronized关键字。这为您提供了性能奖励和简单性。

  • 可以是单例。由于该对象是线程安全的,因此无需为单独的线程提供它们自己的版本。您可以将它传递给几个线程,知道状态不会改变,从而避免您可能不得不花费更多内存来创建副本。

于 2013-10-09T16:49:06.980 回答
0

线程访问对象的问题始终是安全地更改对象。如果您可以保证状态不会同时被更改,那么读取对象的状态本质上是安全的。(显然我的回答假设您正在更改线程代码中的对象。)

不可变对象只是掩盖了这种复杂性——我现在必须保证我持有的引用对象不是陈旧的。此外,如果我拥有的版本确实过时,我现在必须确定该对象的最新版本在哪里。

于 2013-10-09T16:49:25.950 回答
0

我认为这实际上取决于您将使用它们的上下文。它是一个将在多个线程中不断访问的对象吗?系统的硬件限制是什么?

与其说对象是不可变的还是可变的,不如说是关于如何访问它们。如果在访问副本的同时复制它,即使是不可变对象也会导致线程安全问题......

您可能应该查看 ACID: http ://en.wikipedia.org/wiki/ACID

此外,Java 具有可靠且易于实现的同步代码方法,因此可以从多个线程安全地访问可变对象。对于需要在内存消耗方面具有高性能的系统,这可能是要走的路。但同样,这是基于您的限制的权衡 - 内存或速度更重要的是什么?

于 2013-10-09T16:39:01.150 回答