【问题标题】:Concurrency in Hashtables, and fail-safe behavior哈希表中的并发性和故障安全行为
【发布时间】:2013-04-25 02:32:04
【问题描述】:

我正在阅读有关 Hashtables 的内容,这个问题让我印象深刻。哈希表是同步的,那么哈希表上的迭代器是否安全?

此外,对于故障安全集合,当多个线程尝试访问集合时,您不会收到 ConcurrentModificationException,因为每个线程都在处理它自己的副本。我想知道这是否意味着在内存中为每个线程创建了 Collection 的实际物理副本?

【问题讨论】:

  • 请养成添加相关语言标签的习惯。
  • 当然,很抱歉。
  • 澄清一下,ConcurrentModificationException(尽管它的名字)真的与线程无关。线程安全和“修改期间的迭代安全”(CME)是两个正交的概念。

标签: java concurrency thread-safety hashtable


【解决方案1】:

嗯...如果“故障安全”是指您无法获得ConcurrentModificationException,那么答案是否定的。您可以通过@987654324 获得该异常@ 当您在其上使用 Iterator 时。 javadoc 明确指出。


此外,对于故障安全集合,当多个线程尝试访问集合时,您不会收到 ConcurrentModificationException,因为每个线程都在处理它自己的副本。

(您似乎从某个地方引用或总结了这一点。无论如何,我认为它不准确。)

我想知道这是否意味着在内存中为每个线程创建了 Collection 的实际物理副本?

在某些情况下,迭代器正在处理一个不会被修改的副本。在其他情况下,迭代器正在处理可以修改和/或根本不代表集合的一致快照的东西。这实际上取决于如何为您正在谈论的特定集合类指定“故障安全”行为。 (关于集合类的好处之一是它们清楚地指定了您可以依赖和不能依赖的内容。您只需仔细阅读规范以确保您真正理解它们!)


哦,所以 Hashtable 中的迭代器是故障快速的,但枚举器是故障安全的。

第一部分是正确的。第二个不正确。

Hashtable 返回的 Enumeration 对象不保证是故障安全的。 javadocs 的状态是 Enumeration 不是快速失败的。当您修改Hashtable 并同时枚举它时实际发生的情况未指定。 (如果你真的想知道,请查看你正在使用的特定 Java 版本的源代码。)

【讨论】:

  • 哦,所以 Hashtable 中的迭代器是故障快速的,但枚举器是故障安全的。谢谢你。
  • 是的,我在 Java 博客中读到过。但是你的回答让我更加好奇。那么,集合的故障安全行为是如何实际实现的呢?
  • @Setafire - 就像我说的。这取决于指定的行为实际上是什么!没有单一的答案。
  • 只有当“故障安全”意味着它没有告诉您它已损坏且不再工作时,枚举器才是“故障安全”。这并不安全;坦率地说,这与更安全相反。
  • @LouisWasserman - 正是!!!请注意,javadocs 并没有说枚举器/枚举是“故障安全的”。这种说法是某人想象的虚构。事实上,他们添加 Iterator 类的原因之一是为了清理这个烂摊子……通过引入快速失败的行为。在此之前,Hashtable 和 Vector 实例的同时更新和枚举是 bug 的主要来源。
【解决方案2】:

RTFM:

该类的所有“集合视图方法”返回的集合的迭代器方法返回的迭代器是快速失败的:如果在创建迭代器后的任何时间对 Hashtable 进行结构修改,除了通过迭代器自己的remove方法,迭代器会抛出ConcurrentModificationException。因此,面对并发修改,迭代器快速而干净地失败,而不是在未来不确定的时间冒任意的、非确定性的行为。 Hashtable 的键和元素方法返回的枚举不是快速失败的。

请注意,无法保证迭代器的快速失败行为,因为一般来说,在存在不同步的并发修改的情况下无法做出任何硬保证。快速失败的迭代器会尽最大努力抛出 ConcurrentModificationException。因此,编写一个依赖此异常的正确性的程序是错误的:迭代器的快速失败行为应仅用于检测错误。

换句话说,Hashtable 上的迭代并不比不同步的HashMap 上的迭代更安全。

【讨论】:

    【解决方案3】:
    Hashtable<String, Integer> hashtable = new Hashtable<>();
    Integer a = hashtable.put("a", 1);
    Integer a1 = hashtable.put("b", 2);
    Integer a3 = hashtable.put("c", 3);
    Iterator<Map.Entry<String, Integer>> iterator = hashtable.entrySet().iterator();
    while(iterator.hasNext()){
            hashtable.put("d",4);
            iterator.next();
        }
    
    1. 哈希表不是并发的,它是同步的。
    2. Hashtable 是快速失败的,如果您在不使用迭代器的情况下添加或删除一个元素时迭代 Hashtable,那么您将收到 ConcurrentModificationException。

    【讨论】:

      猜你喜欢
      • 2011-02-22
      • 1970-01-01
      • 2017-03-16
      • 1970-01-01
      • 2013-11-12
      • 2015-10-10
      • 2013-06-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多