让你的collections线程安全吗?

在设计集合类时,有没有理由不私下实现锁定以使其线程安全? 或者我应该将该责任留给collections的消费者?

是否有任何理由不私下实现锁定以使其线程安全?

这取决于。 你的目标是编写一个由多个线程访问的集合类吗? 如果是这样,请使其线程安全。 如果没有,不要浪费你的时间。 当人们谈论“过早优化”时,人们会提到这种事情

解决你遇到的问题。 不要试图解决你认为未来几年可能会遇到的未来问题,因为你看不到未来,你总是会犯错。

注意:您仍然需要以可维护的方式编写代码,这样如果您确实需要出现并为集合添加锁定,那就不会非常困难。 我的观点是“不要实现你不需要也不会使用的function”

对于Java,您应该保持不同步的速度。 如果需要,集合的使用者可以包装在同步包装器中 。

线程安全集合可能是骗人的。 Jared Par发表了一些关于线程安全集合的有趣文章:

问题是有几个级别的线程安全集合。 我发现当大多数人说线程安全收集他们真正的意思是“一个集合,当修改和从多个线程访问时不会被破坏”

但是,如果构建数据线程安全列表非常简单,为什么Microsoft不在框架中添加这些标准集合?

答案:ThreadSafeList是一个几乎无法使用的类,因为该设计会引导您走向错误代码的道路。

在您研究如何使用列表之前,此设计中的缺陷并不明显。 例如,如果有第一个元素,则尝试从列表中获取第一个元素。

static int GetFirstOrDefault(ThreadSafeList list) { if (list.Count > 0) { return list[0]; } return 0; } 

此代码是典型的竞争条件。 考虑列表中只有一个>元素的情况。 如果另一个线程在if语句和return语句之间删除了该元素,则return语句将抛出exception,因为它试图访问列表中的无效索引。 即使ThreadSafeList是数据线程安全的,也没有什么能保证在下一次调用同一个对象时一个调用的返回值的有效性

http://blogs.msdn.com/b/jaredpar/archive/2009/02/11/why-are-thread-safe-collections-so-hard.aspx

http://blogs.msdn.com/b/jaredpar/archive/2009/02/16/a-more-usable-thread-safe-collection.aspx

集合类需要尽可能快。 因此,请将锁定保留。

调用代码将知道锁定最佳位置,而集合类则不知道。 在最糟糕的情况下,应用程序必须添加一个额外的锁,这意味着发生了两次锁定,使其成为连续命中的两倍。

我个人会把它留给消费者。 它将使您的集合类更通用。

只需在文档中明确表示您的线程不安全并将其保留,或者,如果您的应用程序需要线程安全,请确保其线程安全并在文档中注明。 唯一的规则是记录它。 除此之外,为您制作课程,如果其他人想要使用它,他们可以。

如果我正在寻找一个集合类并且我需要线程安全function而你的课程没有它们,我会立即跳到下一个提供它们提供的内容。 您的collections将不再受我的关注。

注意开头的“如果”。 有些客户会想要它,有些则不会,有些则不会关心。 如果您要为消费者构建工具包,那么为什么不提供这两种品种呢? 这样我可以选择使用哪一个,但如果我想要线程安全,你仍然会引起我的注意,我不必自己编写。

不使线程安全的主要原因是性能。 线程安全代码可能比非安全代码慢100倍,因此如果您的客户端不需要该function,那将是一个相当大的浪费。

请注意,如果您尝试使任何类线程安全,则需要确定常见的使用方案。

例如,在集合的情况下,仅仅使所有属性和方法单独地线程安全可能对于消费者来说不够好,因为首先读取计数,然后循环或类似,如果读完后计数改变了。

使集合线程安全是杀死Java的Vector和Hashtable类的原因。 如前所述,客户端将它包装在线程安全包装中要容易得多,或者在方法子集上同步数据访问,而不是每次访问类时都进行同步命中。 几乎没有人使用Vector或Hashtable,如果他们这样做,他们会被嘲笑,因为他们的替换(ArrayList和HashMap)世界更快。 这是不幸的,因为我(来自C ++背景)更喜欢“Vector”名称(STL),但ArrayList仍然存在。

基本上,将您的集合设计为线程安全的,并在您的类的两个方法中实现锁定:lock()和unlock()。 在任何需要的地方打电话给他们,但留空。 然后inheritance您的集合,实现lock()和unlock()方法。 一个价格的两个class级。

不使集合线程安全的一个很好的理由是提高单线程性能。 示例:Vector上的ArrayList。 将线程安全性延迟到调用者允许通过避免锁定来优化不同步的用例。

使您的集合线程安全的一个很好的理由是提高multithreading性能。 示例:HashMap上的ConcurrentHashMap。 由于CHM内化了multithreading问题,因此它可以比外部同步更有效地对锁定进行条带锁定以实现更高的并发访问。

这将使得无法同时从多个线程访问集合,即使您知道您触摸的元素未被其他任何人使用。

一个示例是具有基于整数的索引访问器的集合。 每个线程可能从其id知道它可以访问哪些索引值而不必担心脏读/写。

另一种会导致不必要的性能损失的情况是,只从集合中读取数据而不写入数据。

我同意将其留给消费者是正确的方法。 如果为消费者提供了更多的灵活性,以确定是否同步了Collection实例或是否同步了另一个对象。 例如,如果您有两个需要更新的列表,那么使用单个锁将它们放在单个同步块中可能是有意义的。

如果你创建一个集合类,不要让它的线程安全。 这样做很难(例如正确和快速),当你做错了(heisenbugs)时,你的消费者的问题很难调试。

相反,实现一个Collection API并使用Collections.synchronizedCollection(yourCollectionInstance)在需要时获取线程安全的实现。

只需在类javadoc中引用相应的Collections.synchronizedXXX方法; 它将清楚地表明您已经在设计中考虑了线程安全性,并确保消费者拥有可供其使用的线程安全选项。

这是一个好的开始。

线程安全词典

但是你会发现你失去了collections的一个重要特征 – 枚举。 你不能对枚举器进行线程安全,它只是不可行,除非你实现自己的枚举器,它将实例锁保存回集合本身。 我怀疑这会导致严重的瓶颈和潜在的僵局。

从JDK 5开始,如果你需要一个线程安全的集合,我首先要看看java.util.concurrent中已经实现的集合之一是否可行。 正如Java Concurrency In Practice的作者指出的那样(包括编写大部分类的人)正确实现这些是非常困难的,特别是如果性能很重要的话。

引用http://download.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html

并发集合

除了Queues之外,这个包还提供了设计用于multithreading上下文的Collection实现:ConcurrentHashMap,ConcurrentSkipListMap,ConcurrentSkipListSet,CopyOnWriteArrayList和CopyOnWriteArraySet。 当期望许multithreading访问给定集合时,ConcurrentHashMap通常优于同步HashMap,并且ConcurrentSkipListMap通常优于同步TreeMap。 当预期的读取和遍历次数远远超过列表的更新次数时,CopyOnWriteArrayList优于同步的ArrayList。