以线程安全方式发布非线程安全对象字段

时间:2011-07-17 12:20:40

标签: java concurrency thread-safety composition mutability

我遇到了Java并发问题。是的,我看了几乎完全相同标题的问题,但他们似乎都在寻求微妙的不同之处。是的,我已经阅读了 Java Concurrency in Practice 。是的,我可以看到为什么它是该主题的 defacto 参考。是的,我已经阅读了专门针对线程安全类中的发布字段的部分。是的,我仍然会问一个关于Java的并发问题,不管我知道有人会简单地指出我那本书。

这让我感到困惑 - 我知道你可以通过确保具有波动性和/或同步访问的正确读/写命令,以线程安全的方式轻松发布可变原语字段,并且64位原语需要由于读/写操作缺乏原子性,因此具有原子访问权限。我知道在需要在类的字段的特定“快照”上执行的代码块上使用锁。我完全了解原子包和AtomicLong<>等好东西。

但是我仍然对将非线程安全对象作为线程安全类中的字段发布感到困惑。

从我所看到的情况来看,只要你在getter中返回对它的引用,你就可以随时访问对象的内容给调用者,他们可以在任何时候使用它。此外,如果你给一个setter,你允许他们将对象引用设置为一个对象,它们可以在他们使用setter的对象之外控制。

无论如何我都无法解决从非线程安全对象组成线程安全类而不使它们全部私有/受保护并在类中为所有所有非线程安全对象具有该类用户可能想要使用的方法。这听起来像是一个样板噩梦。

我的意思是,如果你返回AtomicReference<>对于getter中的对象,他们可以使用.get()来再次获得对它的非同步访问。

我考虑的另一种方法是让所有getter都根据旧的方法返回非线程安全对象的新副本,这意味着修改将无关紧要,同样适用于setter。但是Java有一个无可救药的复杂系统来克隆对象(浅拷贝与深拷贝与特定拷贝等),这有点让我无法做到这一点。此外,这是非常低效的,它不会比使用设计的语言更快,如Clojure的不变性。事实上,考虑到这些语言允许多条不可变数据在幕后共享相同的数据,它可能会慢得多。

那么,如何以可行的方式编写已发布的非线程安全对象的线程安全类?

提前致谢。

1 个答案:

答案 0 :(得分:4)

如果对不安全对象的引用已转义到周围的线程 - 您无法阻止其他线程改变状态,因此您应该保持引用安全。如果你需要返回复杂的对象,那么将数据设为私有,抛出封装访问和修改的方法并制作线程安全的副本(是的,克隆很麻烦)。

尝试查看http://en.wikipedia.org/wiki/Law_of_Demeter设计原则。 quote:特别是,对象应该避免调用另一个方法返回的成员对象的方法。对于许多使用点作为字段标识符的现代面向对象语言,法则可简单地表述为“仅使用一个点”。也就是说,代码a.b.Method()违反了a.Method()没有的规律。举个简单的例子,当一个人想要遛狗时,指责狗的腿直接走路是愚蠢的。相反,一个人命令狗,让它照顾自己的腿。

ps:我担心这是开放式的问题。