【问题标题】:Difference between Hashtable and Collections.synchronizedMap(HashMap)Hashtable 和 Collections.synchronizedMap(HashMap) 的区别
【发布时间】:2012-02-11 03:19:36
【问题描述】:

据我所知,java.util.Hashtable 同步java.util.Map 接口中的每个方法,而Collections.synchronizedMap(hash_map) 返回一个包装对象,其中包含将调用委托给实际hash_map 的同步方法(如果我错了,请纠正我)。

我有两个问题:

  1. 同步每个方法和拥有一个包装类有什么区别?有哪些场景可以选择其中一个?

  2. 当我们执行Collections.synchronizedMap(hash_table) 时会发生什么?这是否等同于简单地使用普通的java.util.Hashtable

【问题讨论】:

  • 这不是 that 问题的重复。这是将 HashMap(或 HashTable)上的同步包装器与 HashTable 进行比较。
  • 我很想知道性能上是否存在差异,目前的答案都没有解决。通过 Map 类型的变量调用 Collections.synchronizedMap 的方法意味着在实现之前有 2 个接口虚拟调用,而使用 Hashtable,最多有 1 个虚拟调用,您可以直接将变量声明为 Hashtable 类型,并使用 0 个虚拟呼叫来完成。但也许 HashMap 在其他方面有所不同,使其整体速度更快。

标签: java collections synchronization hashmap hashtable


【解决方案1】:

我在这两个类的实现中可以发现的另一个区别如下:

Hashtable 类的所有方法都是同步的,即锁定是在方法级别完成的,因此可以说在Hashtable 对象( this) 级别。

• 方法Collections.synchronizedMap(Map) 返回SynchronizedMap 的实例,它是Collections 类的内部类。这个类的所有方法都在一个带有互斥锁的Synchronized 块中。不同之处在于这里的互斥锁。内部类SynchronizedMap 有两个构造函数,一个只接受Map 作为参数,另一个接受MapObject(互斥)作为参数。默认情况下,如果使用第一个仅传递 Map 的构造函数,则 this 被用作互斥体。不过,允许开发人员传递另一个互斥对象作为第二个参数,通过该参数,Map 方法上的锁定将仅在该Object 上,因此限制比Hashtable 更少。

• 因此,Hashtable 使用方法级同步,但Collections.synchronizedMap(Map) 为开发人员提供了使用Synchronized 块锁定提供的互斥锁的灵活性。

【讨论】:

  • 多么漂亮的答案,强调了其他答案中未触及的内容。 +1
  • 额外的构造函数和块级别的同步(不像HashTable的方法级别)是关键。
  • 这真是个有趣的信息,但是内部的 SynchronizedMap 类是私有的,并且没有对应于接受互斥对象作为参数的构造函数版本的静态方法,因此开发人员无法真正提供一个自定义互斥锁,对吧?
  • @CarLuva 你是对的。我没有看到任何暴露于使用第二个构造函数的方法。
  • 我是否正确理解HashtableCollections.synchronizedMap 都是阻塞集合并且在底层几乎相同?
【解决方案2】:

以下是我从一些(希望是正确的)研究中得到的答案:

  1. 两者都提供相同程度的同步。如果您通过 Collections.synchronized 包装 Hashtable,您将拥有相同的程度,但具有另一个冗余层。

  2. HashtableCollections.synchronizedMap(HashMap) 之间的主要区别更多地存在于 API 级别。因为Hashtable 是Java 遗留代码的一部分,您会看到Hashtable API 被增强以实现Map 接口,成为Java 集合框架的一部分。这意味着如果您将Hashtable 包装到Collections.synchronizedMap(),则包装的Hashtable 的API 将仅限于Map API。因此,如果 Hashtable 的 API 包含在您对 行为 的定义中,那么它显然会被更改/限制。

【讨论】:

  • 此外,Hashtable 方法是同步的,而 Synchronized map 的方法实现在互斥锁上具有同步映射。
【解决方案3】:

Java 类中出现的第一个关联集合类 库是 Hashtable,它是 JDK 1.0 的一部分。提供哈希表 一种易于使用、线程安全的关联映射功能,它是 当然方便。然而,线程安全是有代价的—— Hashtable 的所有方法都是同步的。那个时候不争气 同步具有可衡量的性能成本。继任者 Hashtable,HashMap,作为 Collections 的一部分出现 JDK 1.2 中的框架,通过提供 非同步基类和同步包装器, Collections.synchronizedMap。将基本功能与 线程安全的 Collections.synchronizedMap 允许需要的用户 同步拥有它,但不需要它的用户没有 付钱。

Hashtable 和 Hashtable 采用的简单同步方法 synchronizedMap -- 同步 Hashtable 或 同步地图包装对象——有两个主要缺陷。它 是可扩展性的障碍,因为只有一个线程可以访问 一次哈希表。同时,还不够 在许多常见的复合操作中提供真正的线程安全 仍然需要额外的同步。虽然简单的操作如 因为 get() 和 put() 可以安全地完成而无需额外的 同步,有几种常见的操作序列, 例如迭代或 put-if-absent,仍然需要外部 同步以避免数据竞争。

以下链接为出处,有更多信息:Concurrent Collections Classes

【讨论】:

  • 第二段说HashtablesynchronizedMap是一回事。这意味着我们根本没有回答这个问题:它们有什么区别?
【解决方案4】:

区别不在于明显的 API 级别,在实现级别还有很多细微之处。例如,Hashtable 不支持HashMap 对提供的键的哈希码进行高级重新计算以减少哈希冲突。另一方面,Hashtable#hashCode() 避免了自引用哈希表的无限递归,以允许“具有自引用哈希表的某些 1.1 时代小程序工作”。

不过,一般来说,不应该指望Hashtable 在基本正确性和向后兼容性之外获得任何进一步的改进或改进。它被认为是 Java 历史悠久的遗物。

【讨论】:

  • 问题不在于比较hashtable和hashmap
  • 是的:synchronizedMap(new HashMap<>()) vs. new Hashtable<>()
【解决方案5】:

另一个需要注意的区别是 HashTable 不允许空键或空值,而 HashMap 允许一个空键和任意数量的空值。由于 synchronizedMap 是 HashMap 的包装器,因此它对于 null 键和值的行为与 HashMap 相同。

【讨论】:

  • 无关答案
【解决方案6】:

冒着陈述明显(或完全错误)的风险并不是区别

同步包装器添加了自动同步 (线程安全)到任意集合

http://docs.oracle.com/javase/tutorial/collections/implementations/wrapper.html 继续说

以这种方式创建的集合与 通常同步的集合,例如 Vector。

您可能希望查看此线程以了解有关 HashMap 和并发性的问题 - Hashmap concurrency issue(或者您可能已经非常了解它们)。一个很好的例子是:

HashMap 不满足你描述的条件。自从 更新地图的过程不是原子的你可能会遇到地图 处于无效状态。多次写入可能会使其处于损坏状态 状态。 ConcurrentHashMap(1.5 或更高版本)可以满足您的需求。

https://stackoverflow.com/a/1003071/201648

我想就“我什么时候应该使用它”而言,我倾向于在需要并发的地方使用同步集合,否则你可能会为自己创造更多的工作(见下文)。

在改变行为方面

如果使用显式迭代器,则必须调用迭代器方法 从同步块内。不遵循此建议可能 导致不确定的行为

使用提供的 (Oracle) 链接中给出的同步会产生更多后果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-11
    • 2010-11-15
    • 2012-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-02
    • 2015-07-18
    相关资源
    最近更新 更多