ConcurrentHashMap
和 Hashtable
都是 Java 中线程安全的键值对集合,但它们在实现方式、性能和使用场景上有显著区别。以下是两者的主要区别:
1. 锁的粒度
- Hashtable:
- 使用全局锁(
synchronized
修饰方法),即对整个哈希表加锁。 - 在多线程环境下,同一时间只能有一个线程操作
Hashtable
,其他线程会被阻塞,导致性能较差。
- 使用全局锁(
- ConcurrentHashMap:
- 使用分段锁(JDK 7)或 CAS +
synchronized
(JDK 8),锁的粒度更细。 - 在 JDK 7 中,
ConcurrentHashMap
将数据分成多个段(Segment
),每个段独立加锁,不同段可以并发操作。 - 在 JDK 8 中,
ConcurrentHashMap
取消了分段锁,改为对每个桶(Node
)使用synchronized
或 CAS 操作,进一步提高了并发性能。
- 使用分段锁(JDK 7)或 CAS +
2. 并发性能
- Hashtable:
- 由于使用全局锁,并发性能较差,尤其是在高并发场景下,线程竞争激烈,性能下降明显。
- ConcurrentHashMap:
- 由于锁的粒度更细,允许多个线程同时读写不同的段或桶,并发性能显著优于
Hashtable
。 - 在 JDK 8 中,
ConcurrentHashMap
的并发性能进一步提升,接近无锁的性能。
- 由于锁的粒度更细,允许多个线程同时读写不同的段或桶,并发性能显著优于
3. 空值支持
- Hashtable:
- 不允许键或值为
null
,否则会抛出NullPointerException
。
- 不允许键或值为
- ConcurrentHashMap:
- 不允许键或值为
null
,否则会抛出NullPointerException
。 - 这是因为在并发环境下,
null
值的二义性(表示键不存在或值为null
)会导致歧义。
- 不允许键或值为
4. 迭代器的弱一致性
- Hashtable:
- 迭代器是强一致性的,即在迭代过程中,其他线程不能修改
Hashtable
,否则会抛出ConcurrentModificationException
。
- 迭代器是强一致性的,即在迭代过程中,其他线程不能修改
- ConcurrentHashMap:
- 迭代器是弱一致性的,即在迭代过程中,其他线程可以修改
ConcurrentHashMap
,迭代器不会抛出异常,但可能反映部分修改。
- 迭代器是弱一致性的,即在迭代过程中,其他线程可以修改
5. 实现复杂度
- Hashtable:
- 实现简单,直接使用
synchronized
修饰方法。
- 实现简单,直接使用
- ConcurrentHashMap:
- 实现复杂,JDK 7 使用分段锁,JDK 8 使用 CAS +
synchronized
,需要处理更多的并发问题。
- 实现复杂,JDK 7 使用分段锁,JDK 8 使用 CAS +
6. 适用场景
- Hashtable:
- 适用于低并发场景,或者需要强一致性的场景。
- ConcurrentHashMap:
- 适用于高并发场景,性能要求较高的场景。
总结对比:
特性 | Hashtable | ConcurrentHashMap |
---|---|---|
锁的粒度 | 全局锁(整个表加锁) | 分段锁(JDK 7)或桶锁(JDK 8) |
并发性能 | 较差 | 较好 |
空值支持 | 不允许键或值为 null | 不允许键或值为 null |
迭代器一致性 | 强一致性 | 弱一致性 |
实现复杂度 | 简单 | 复杂 |
适用场景 | 低并发场景 | 高并发场景 |
示例代码:
Hashtable
Hashtable<String, String> hashtable = new Hashtable<>();
hashtable.put("key1", "value1");
hashtable.put("key2", "value2");
// 多线程操作
new Thread(() -> {
hashtable.put("key3", "value3");
}).start();
new Thread(() -> {
System.out.println(hashtable.get("key1"));
}).start();
ConcurrentHashMap
ConcurrentHashMap<String, String> concurrentHashMap = new ConcurrentHashMap<>();
concurrentHashMap.put("key1", "value1");
concurrentHashMap.put("key2", "value2");
// 多线程操作
new Thread(() -> {
concurrentHashMap.put("key3", "value3");
}).start();
new Thread(() -> {
System.out.println(concurrentHashMap.get("key1"));
}).start();
总结:
Hashtable
是早期的线程安全集合,实现简单但性能较差。ConcurrentHashMap
是现代化的高并发集合,通过细粒度锁或 CAS 机制提供了更好的并发性能。- 在高并发场景下,优先选择
ConcurrentHashMap
。
THE END
暂无评论内容