对接口和同步集合进行编程

时间:2009-10-19 21:24:34

标签: java data-structures synchronization

这个问题与Java集合有关 - 特别是Hashtable和Vector - 但也可能适用于其他地方。

我在很多地方都读到了编程接口的好处,我同意100%。例如,在不考虑底层实现的情况下编程到List接口的能力肯定有助于解耦和测试目的。考虑到内部存储结构,随机访问时间等方面的差异,我可以看到ArrayList和LinkedList如何在不同的环境下应用。但是,这两个实现可以在同一个接口下使用...很棒。

我似乎无法放置某些同步实现(特别是Hashtable和Vector)如何适应这些接口。对我来说,他们似乎不适合这个模型。大多数底层数据结构实现似乎在数据存储方式(LinkedList,Array,排序树等)方面有所不同,而同步则处理可以访问数据的条件(锁定条件)。让我们看一个方法返回Map集合的例子:

public Map<String, String> getSomeData();

让我们假设应用程序根本不关心并发性。在这种情况下,我们对该方法通过接口返回的任何实现进行操作......每个人都很高兴。世界是稳定的。

然而,如果应用程序现在需要关注并发性前端怎么办?我们现在无法在不考虑底层实现的情况下运行 - Hashtable会很好,但是必须满足其他实现。让我们考虑3个场景:

1)在使用集合添加/删除时,使用同步块等强制执行同步。但是,如果返回同步实现(Hashtable),这不会有点过分吗?

2)更改方法签名以返回Hashtable。然而,这将我们与Hashtable实现紧密联系在一起,因此,编程到界面的优势被抛到窗外。

3)使用并发包并更改方法签名以返回ConcurrentMap接口的实现。对我来说,这似乎是前进的方向。

基本上,似乎某些同步实现在集合框架中有点不合适,因为在对接口进行编程时,同步问题几乎迫使人们考虑底层实现。

我完全忽略了这一点吗?

感谢。

4 个答案:

答案 0 :(得分:6)

1)是的,这将是矫枉过正的 2)正确,不应该这样做 3)取决于具体情况。

正如您所知,对接口进行编程描述了应用程序的功能(而不是它是如何实现的,即实现)

同步已从后续实现中删除(请记住,Vector和Hastable之前的java 1.2之前是ArrayList和HasMap未同步,但它们都分别实现了List和Map接口),因为它们导致性能损失到期过度同步。例如,如果在单个线程中使用向量,则仍然可以在该单个线程中进行同步。

设计应用程序时,必须考虑在多个线程之间共享数据结构。在那里,您将选择将要使用的方法,并且您将选择谁负责保持数据状态清洁。

您可以在此处选择您提到的选项1或3。会有手动同步吗?我们应该使用同步接口吗?我们将支持哪个版本等等。

例如,如果你选择1,你也可以在你的设计中拒绝某些实现(即向量)

数据同步不是由“运气”发生的事情,你必须设计它才能正确发生并且不会引起它解决的问题。

在此设计过程中,您应该注意您将使用的选项(实现)和/或底层基础结构。

避免过度同步的最简单方法是使用不可变数据,不要与其他线程共享数据。

与Martin Fowler分配计算的第一定律非常相似:

  

“因此,我们达到了我的分布式对象设计第一定律:不要分发你的对象。”

多线程应用程序的第一定律是:

  

多线程应用程序的第一定律:不共享数据?

:)

最后注意:Collections类提供某些接口的“同步”版本:

Synchronized List
Synchronized Map
Synchronized Set

答案 1 :(得分:1)

您正在努力的事实是,在多线程环境中,客户端无法天真地使用具有可变共享状态的对象。集合接口本身不会告诉您如何安全地使用对象。返回ConcurrentMap有助于提供一些其他信息,但仅针对该特定情况。

通常,您必须在文档(例如,javadoc)中单独传达线程安全问题,或者使用自定义注释,如Java Concurrency in Practice.中所述。返回对象的客户端必须使用自己的锁定机制或你提供的一个。接口通常与线程安全性正交。

如果客户端知道所有实现都来自Concurrent实现,那么这不是问题,但该信息不是由接口本身传达的。

答案 2 :(得分:0)

Java的VectorHashtable早于JDK 5中添加的当前并发包。在编写Vector时,人们认为让它同步是个好主意,然后他们可能在企业使用中遇到了性能障碍。并发肯定是代码到接口模块化可能并不总能解决的情况之一。

答案 3 :(得分:0)

来自JDK 1.0的

HashtableVector非常陈旧。它们早于JDK1.2的标准集合,并且很久以前就不应该在新代码中使用。请改用HashMapArrayList包裹的Collections.synchronizedMap()Collections.synchronizedList()

您可以在Since标记下的API文档中看到JDK中引入某些内容时的版本。