使集合具有线程安全性?

时间:2020-03-06 15:02:41  来源:igfitidea点击:

在设计收集类时,是否有理由不私下实现锁定以使其线程安全?还是我应该将这一责任留给收藏的消费者?

解决方案

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

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

只需在文档中明确说明我们没有使它成为线程安全的,就不予理会,或者如果对于应用程序,我们希望它具有线程安全性,使其成为线程安全的并在文档中加以说明。唯一的规则是对其进行记录。除此之外,为我们创建课程,如果其他人想要使用它,他们可以。

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

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

另一种可能会导致不必要的性能下降的情况是,仅从集合中读取数据而未将数据写入时。

如果我正在寻找一个集合类,并且我需要线程安全功能,而类没有这些功能,那么我将立即跳到那里的下一个产品中,看看它们提供了什么。收藏不会再引起我的注意。

请注意开头的"如果"。有些客户会想要它,有些则不会,有些则不在乎。如果我们要为消费者构建工具包,那么为什么不同时提供这两种产品呢?这样,我可以选择要使用的那个,但是如果我想要线程安全的,我们仍然会关注我,而不必自己编写。

收集类需要尽可能快。因此,将锁排除在外。

调用代码将知道集合类没有最好的锁。在最坏的情况下,应用程序将不得不添加一个额外的锁,这意味着将发生两个锁,这使其性能命中率翻倍。

不使其成为线程安全的主要原因是性能。线程安全代码可能比非安全代码慢100倍,因此,如果客户不希望使用该功能,那将是一个很大的浪费。

我同意将它留给消费者是正确的方法。 If为Collection实例同步还是其他对象同步提供了更大的灵活性。例如,如果我们有两个都需要更新的列表,则可以使用一个锁将它们放在单个同步块中。

如果要创建一个收集类,请不要使其成为线程安全的。正确(例如正确和快速)的执行相当困难,而如果我们做错了(heisenbugs),消费者的问题就很难调试。

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

只需在类javadoc中引用相应的Collections.synchronizedXXX方法即可;这将清楚表明我们在设计中已考虑了线程安全性,并确保使用者可以使用线程安全选项。

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

例如,对于集合而言,仅使所有属性和方法分别具有线程安全性可能对消费者而言不够好,因为如果先读取计数,然后循环或者类似操作,如果对读取后计数发生变化。

is there any reason not to implement locking privately to make it thread safe?

这取决于。目标是编写一个可以被多个线程访问的集合类吗?

如果是这样,请使其成为线程安全的。如果没有,请不要浪费时间。人们谈论"过早优化"时所指的就是这种东西

解决我们遇到的问题。不要试图解决我们认为未来可能会有几年的未来问题,因为我们看不到未来,而且我们总是会错的。

注意:我们仍然需要以可维护的方式编写代码,这样,如果确实需要对集合进行锁定并添加锁,就不会很难。我的观点是"不要实现我们不需要且不会使用的功能"

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

基本上,将集合设计为线程安全的,并在类的两种方法中实现锁定:lock()和unlock()。在需要的任何地方调用它们,但将它们留空。然后,对集合进行子类化,以实现lock()和unlock()方法。价格为两节课。

不使集合成为线程安全的一个很好的理由是为了提高单线程性能。示例:Vector上的ArrayList。将线程安全性推迟给调用方,可以通过避免锁定来优化不同步的用例。

使集合具有线程安全性的一个很好的理由是提高了多线程性能。示例:通过HashMap的ConcurrentHashMap。由于CHM内部化了多线程问题,因此与外部同步相比,它可以更有效地剥离锁定以实现更大的并发访问。

这是一个好的开始。

线程安全词典

但是我们会注意到,我们失去了集合枚举的重要功能之一。我们不能对枚举器进行线程安全,这实际上是不可行的,除非我们实现自己的枚举器,该枚举器将实例锁保留到集合本身。我怀疑这会导致严重的瓶颈和潜在的僵局。

The problem is there are several
  levels of thread safe collections.  I
  find that when most people say thread
  safe collection what they really mean
  “a collection that will not be
  corrupted when modified and accessed
  from multiple threads”
  
  ...
  
  But if building a data thread safe
  list is so easy, why doesn’t Microsoft
  add these standard collections in the
  framework?
  
  Answer: ThreadSafeList is a
  virtually unusable class because the
  design leads you down the path to bad
  code.
  
  The flaws in this design are not
  apparent until you examine how lists
  are commonly used.  For example,  take
  the following code which attempts to
  grab the first element out of the list
  if there is one.
static int GetFirstOrDefault(ThreadSafeList<int> list) {
    if (list.Count > 0) {
        return list[0];
    }
    return 0; }
This code is a classic race condition.  Consider the case where there is only one > element in the list.  If another thread removes that element in between the if statement and the return statement, the return statement will throw an exception because it’s trying to access an invalid index in the list.  Even though ThreadSafeList is data thread safe, there is nothing guaranteeing the validity of a return value of one call across the next call to the same object

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

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

段落数量不匹配