8

我遇到了 Java 并发问题。是的,我查看了标题几乎完全相同的问题,但它们似乎都在问微妙的不同问题。是的,我读过Java Concurrency in Practice。是的,我明白为什么它是该主题的实际参考。是的,我已经阅读了专门关于在线程安全类中发布字段的部分。是的,我仍然会问一个关于 Java 的并发问题,尽管我知道有人会简单地将我指向那本书。

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

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

从我所见,只要您在 getter 中返回对它的引用,您就可以向调用者提供对对象内容的前所未有的访问权限,他们可以在任何时候使用。此外,如果您提供一个 setter,则您允许他们将对象引用设置为他们可以在他们使用 setter 的对象之外潜在地控制的对象。

无论如何,如果不将它们全部设为私有/受保护并在类中为所有非线程安全对象的所有方法创建线程安全包装器方法,我就无法从非线程安全对象中组成线程安全类有该类的用户可能想要使用的。这听起来就像一个样板的噩梦。

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

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

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

提前致谢。

4

1 回答 1

4

如果对不安全对象的引用已经逃逸到周围的线程 - 你不能做任何事情来阻止其他线程改变状态,所以你应该保持引用安全。如果您需要返回复杂的对象,则将数据设为私有,引入封装访问和修改的方法并制作线程安全的副本(是的,克隆很麻烦)。

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

ps:恐怕这是开放式问题。

于 2011-07-17T12:33:20.197 回答