Hashtable, Collections.SynchronizedMap和ConcurrentHashMap线程安全实现原理的区别以及性能测试

Hashtable,Collections.SynchronizedMap和ConcurrentHashMap线程安全实现原理的区别以及性能测试

这三种 Map 都是 Java 中比较重要的集合类,虽然前两个不太常用,但是因为与多线程相关,所以关于这几种 Map 的对比已经成为了 Java 面试时的高频考点。首先要说明的是,其中每一个单独拎出来都足够支撑一篇长篇大论的技术文章,所以本文把重点放在了这三种集合类的线程安全实现原理的对比以及性能测试上,其他细节不做深入探讨。

一、线程安全原理对比

1. Hashtable

首先必须吐槽一下这个类名,作为官方工具类竟然不符合驼峰命名规则,怪不得被弃用了,开玩笑哈哈,主要原因还是性能低下,那 Hashtable 的性能为什么低下呢,这个嘛只需要看一下它的源码就一目了然了,以下是 Hashtable 中几个比较重要的方法:

public synchronized V put(K key, V value) {
   ...}
public synchronized V get(Object key) {
   ...}
public synchronized int size() {
   ...}
public synchronized boolean remove(Object key, Object value) {
   ...}
public synchronized boolean contains(Object value) {
   ...}
... ...

查看源码后可以看出,Hashtable 实现线程安全的原理相当简单粗暴,直接在方法声明上使用 synchronized 关键字。这样一来,不管线程执行哪个方法,即便只是读取数据,都需要锁住整个 Hashtable 对象,可想而知其并发性能必然不会太好。

2. Collections.SynchronizedMap

SynchronizedMapCollections 集合类的私有静态内部类,其定义和构造方法如下:

private static class SynchronizedMap<K,V> implements Map<K,V>, Serializable {
   
        private static final long serialVersionUID = 1978198479659022715L;
    	  // 用于接收传入的Map对象,也是类方法操作的对象
        private final Map<K,V> m;     
    	  // 锁对象
        final Object mutex;   
  
  			// 以下是SynchronizedMap的两个构造方法
        SynchronizedMap(Map<K,V> m) {
   
            this.m = Objects.requireNonNull(m);
            mutex = this;
        }
        SynchronizedMap(Map<K,V> m, Object mutex) {
   
            this.m = m;
            this.mutex = mutex;
        }
}
  • SynchronizedMap 一共有三个成员变量,序列化ID抛开不谈,另外两个分别是 Map 类型的实例变量 m,用于接收构造方法中传入的 Map 参数,以及 Object 类型的实例变量 mutex,作为锁对象使用。

  • 再来看构造方法,SynchronizedMap 有两个构造方法。第一个构造方法需要传入一个 Map 类型的参数,这个参数会被传递给成员变量 m,接下来 SynchronizedMap 所有方法的操作都是针对 m 的操作,需要注意的是这个参数不能为空,否则会由 Objects 类的 requireNonNull() 方法抛出空指针异常,然后当前的 SynchronizedMap 对象 this 会被传递给 mutex 作为锁对象;第二个构造方法有两个参数,第一个 Map 类型的参数会被传递给成员变量 m,第二个 Object 类型的参数会被传递给 mutex 作为锁对象。

  • 最后来看 SynchronizedMap 的部分主要方法:

    public int size() {
         
        synchronized (mutex) {
         return m.size();}
    }
    public boolean isEmpty() {
         
        synchronized (mutex) {
         return m.isEmpty();}
    }
    public boolean containsKey(Object key) {
         
        synchronized (mutex) {
         return m.containsKey(key);}
    }
    public V get(Object key) {
         
        synchronized (mutex) {
         return m.get(key);}
    }
    public V put(K key, V value) {
         
        synchronized (mutex) {
         return m.put(key, value);}
    }
    public V remove(Object key) {
         
        synchronized (mutex) {
         return m.remove(key);}
    }
    

从源码可以看出,SynchronizedMap 实现线程安全的方法也是比较简单的,所有方法都是先对锁对象 mutex 上锁,然后再直接调用 Map 类型成员变量 m 的相关方法。这样一来,线程在执行方法时,只有先获得了 mutex 的锁才能对 m 进行操作。因此,跟 Hashtable 一样,在同一个时间点,只能有一个线程对 SynchronizedMap 对象进行操作,虽然保证了线程安全,却导致了性能低下。这么看来,连 Hashtable 都被弃用了,那性能同样低下的 SynchronizedMap 还有什么存在的必要呢?别忘了,后者的构造方法需要传入一个 Map 类型的参数,也就是说它可以将非线程安全的 Map 转化为线程安全的 Map,而这正是其存在的意义,以下是 SynchronizedMap 的用法示例 (这里并没有演示多线程操作):

Map<String
### Collections.synchronizedMapConcurrentHashMap Hashtable区别 #### 线程安全性机制 `Collections.synchronizedMap` 方法返回一个同步的 (线程安全的) 映射,该映射的方法被同步锁保护。这意味着每次只有一个线程可以访问映射中的方法调用[^1]。 相比之下,`ConcurrentHashMap` 提供了一种更细粒度的锁定机制,在大多数并发场景下性能更好。它允许多个读取操作并行执行,并发地处理多个桶(buckets),而不仅仅是整个表级别的独占锁。 `Hashtable` 类似于 `synchronizedMap` 返回的对象,所有的主要公共方法都被声明为 `synchronized`,因此也是完全同步化的;但是其内部实现细节与现代集合框架有所不同[^2]。 #### 性能特性 由于采用了分段锁技术,`ConcurrentHashMap` 在高并发环境下通常具有更好的吞吐量。对于只读操作或者少量更新的情况尤其明显,因为这些情况下几乎不需要加锁开销。 另一方面,当使用 `Collections.synchronizedMap` 或者 `Hashtable` 时,即使只是获取键值对这样的简单查询也需要等待其他写入完成才能继续,这可能导致不必要的延迟较低的整体效率。 #### 功能差异 值得注意的是,除了基本的功能外,`ConcurrentHashMap` 还提供了额外的一些实用工具类方法来支持原子性的复合动作,比如计算特定条件下的删除或替换等复杂逻辑而不必担心竞态条件问题。 然而,无论是通过 `Collections.synchronizedMap` 创建的地图还是原生的 `Hashtable` 都缺乏这种高级别的抽象层次的支持。 ```java // 使用 ConcurrentMap 接口提供的 replace() 方法 concurrentHashMap.replace(key, oldValue, newValue); ``` #### 设计哲学的不同 从设计角度来看,`Hashtable` 是较早版本 JDK 中的一部分,后来随着 Java 平台的发展逐渐被推荐使用更加灵活高效的替代品所取代。尽管如此,出于向后兼容的原因仍然保留了下来。 综上所述,如果应用程序确实存在多线程环境下的共享数据结构需求,则应优先考虑采用 `ConcurrentHashMap` 来代替传统的解决方案如 `Hashtable` 或基于 `Collections.synchronizedMap()` 构建的方式,以获得更高的可扩展性响应速度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值