我目前正在学习java中的线程,从我读过的内容来看,实现它们并不是问题,而是让代码线程安全。这让我想到了一个问题:我应该尽可能使用不可变对象来确保防止并发错误吗?
我已经阅读了关于何时使用可变对象的不同意见,包括:
If immutable objects are good, why do people keep creating mutable objects?(Programmers.SE)
对于大型和/或复杂对象,为每次更改创建对象的新副本可能非常昂贵和/或繁琐。对于具有不同身份的对象,更改现有对象比创建新的已修改副本更加简单直观。
Immutable Objects(Java教程)
对象创建的影响经常被高估,并且可以通过与不可变对象相关联的一些效率来抵消。这些包括由于垃圾收集而减少的开销,以及消除保护可变对象免受损坏所需的代码。
那么,在实现线程时是否有最佳实践?我应该尽可能尝试使用不可变对象吗?
答案 0 :(得分:3)
使用真正不可变的对象(没有包含对可变类的引用的字段)是线程安全的,并且在实际可行时使用它们是一个好习惯,但是不可变性是否可行取决于具体情况;例如,某些工具包需要setter或field injection,因此不能与不可变对象一起使用。使用 Value Object 模式,不可变性通常是最实用的,其中对象没有任何特别复杂的行为,并且基本上封装了一个清晰可描述的值(String
,原始包装器,Guava的Immutable*
收藏品。)
通常,我尝试在实际的情况下使用简单的字段使对象变为不可变的,并且我发现从默认情况开始,不可变对象是最安全的并且只有在需要它们的特定原因时才使用mutator是有帮助的。
同样,如果您要依赖类的不变性来处理线程安全性,那么通常应该声明类final
以避免各种令人头疼的问题。我并没有坚持所有类都必须是abstract
或final
,但final
可实例化的类是一个很好的默认类。
答案 1 :(得分:1)
是的,多线程时,不可变对象通常是你的朋友。
有一些很大的优点:
线程安全的。如果你不能改变它的状态,那么两个线程不可能同时更新它或看到它的不同版本。
没有锁定。由于没有任何变化,因此无需使用synchronized
关键字。这为您提供了性能奖励和简单性。
可以是单身人士。由于该对象是线程安全的,因此不需要为它们自己的版本提供单独的线程。您可以将其传递给多个线程,因为它们知道状态不会发生变化,从而使您不必花费更多内存来创建副本。
答案 2 :(得分:0)
我认为这实际上取决于你将使用它们的背景。它是一个将在多个线程中不断访问的对象吗?系统的硬件限制是什么?
这不是关于对象是不可变还是可变的,而是关于如何访问它们。即使是不可变对象,如果在访问副本的同时复制它也会导致线程安全问题......
您应该查看ACID: http://en.wikipedia.org/wiki/ACID
此外,Java具有可靠且易于实现的同步代码方法,因此可以从多个线程安全地访问可变对象。对于需要在内存消耗方面具有高性能的系统,这可能是最佳选择。但是,再次根据你的限制进行权衡 - 更重要的记忆或速度是什么?
答案 3 :(得分:0)
线程访问对象的问题始终是安全地更改对象;如果可以保证状态不会同时被更改,则读取对象的状态本质上是安全的。 (显然我的回答是假设您正在使用线程代码更改对象。)
不可变对象只是掩盖了这种复杂性 - 我现在必须保证我持有引用的对象不是陈旧的。另外,如果我的版本确实陈旧,我现在必须确定该对象的最新版本在哪里。