Java: HashMap 和 ConcurrentHashMap的区别

这篇文章资料来自于网络,是对部分知识整理,这里只是记录一下,仅供参考

HashMap 是非线程安全的,适用于单线程环境,而ConcurrentHashMap 是线程安全的,通过分段锁或CAS+synchronized+红黑树等机制实现高并发下的数据安全和性能。此外,HashMap 允许null 值,而ConcurrentHashMap 不允许。

线程安全与锁机制

**HashMap:非线程安全,**在多线程环境下操作可能导致数据不一致或异常。它实际上是将整个Map 作为一个整体来加锁,所有线程都争夺同一个锁。

ConcurrentHashMap:线程安全。

  • JDK 1.7:采用分段锁(Segment)的机制,每个段独立加锁,允许不同线程同时访问不同段的数据,提高了并发性能。
  • JDK 1.8:采用数组+链表+红黑树的结构,并通过CAS(比较并交换)和synchronized 关键字保证线程安全,锁的粒度更小,只针对需要操作的Node(节点)加锁。

数据结构

HashMap:在JDK 1.8 及以后版本,同样采用数组+链表+红黑树的结构,相比于旧版本优化了查找性能。

ConcurrentHashMap:在JDK 1.7 版本中使用的是数组+链表,而到了JDK 1.8,其数据结构与JDK 1.8 的HashMap 类似,也是数组+链表+红黑树。

null 值

HashMap:允许null 作为键和值。

ConcurrentHashMap:不允许null 作为键或值。

性能

HashMap:在低并发环境下性能可能略优,因为它没有同步开销。

ConcurrentHashMap:在高并发环境下性能表现更优,尤其是对读多写少的场景。

什么是分段锁(JDK 1.7:HashMap 采用分段锁(Segment))

分段锁(Segmented Locking)是一种用于提高多线程程序性能的锁机制,通过将锁细分来减少竞争,从而在高并发环境中提高性能。分段锁在Java的ConcurrentHashMap中有广泛应用。

分段锁的工作原理

分段锁的基本思想是将锁分为多个段(Segment),每个段独立加锁,这样在并发环境下,不同的线程可以同时操作不同的段,从而减少锁竞争,提高并发访问率。相比于一个全局锁来说,分段锁提供了更细粒度的锁控制,允许更高的并发。

Java中分段锁的示例

以一个简化版本的基于分段锁思想实现的ConcurrentHashMap为例,来演示分段锁的实现。请注意,这里的实现是为了演示分段锁的概念,而非完整的ConcurrentHashMap实现。

java 复制代码
import java.util.HashMap;
import java.util.Map;

public class SegmentedHashMap<K, V> {
    // 分段锁的数量,通常设为2的n次方
    private static final int SEGMENTS = 16;
    private final Segment<K, V>[] segments;

    // 初始化所有的段
    @SuppressWarnings("unchecked")
    public SegmentedHashMap() {
        segments = (Segment<K, V>[]) new Segment[SEGMENTS];
        for (int i = 0; i < SEGMENTS; i++) {
            segments[i] = new Segment<>();
        }
    }

    // 根据key获取对应段的索引
    private int getSegmentIndex(K key) {
        return key.hashCode() & (SEGMENTS - 1);
    }

    // 放入键值对
    public void put(K key, V value) {
        int index = getSegmentIndex(key);
        segments[index].put(key, value);
    }

    // 根据key获取值
    public V get(K key) {
        int index = getSegmentIndex(key);
        return segments[index].get(key);
    }

    // 每个段的实现
    private static class Segment<K, V> {
        private final Map<K, V> map = new HashMap<>();
        private final Object lock = new Object(); // 每个段的锁

        // 放入键值对,加锁以保证线程安全
        public void put(K key, V value) {
            synchronized (lock) {
                map.put(key, value);
            }
        }

        // 根据key获取值,加锁以保证线程安全
        public V get(K key) {
            synchronized (lock) {
                return map.get(key);
            }
        }
    }
}

在这个示例中,SegmentedHashMap通过一个Segment数组实现了分段锁。每个Segment独立加锁,从而允许多线程可以同时对不同的段进行操作。通过getSegmentIndex(K key)方法根据键的哈希码计算出键所在的段的索引,从而确定操作哪一个段。

分段锁的优势

  • 高并发性能:在多线程环境下,通过减少锁的竞争,提高了并发性能。
  • 减少阻塞时间:线程操作不同的段时可以同时进行,从而减少了线程阻塞的时间。

分段锁的劣势

  • 内存开销:每个段都有自己的锁,相比于单一锁,分段锁会有更多的内存开销。
  • 实现复杂性:分段锁的实现比单一锁复杂,需要仔细设计段的数量和大小,以及如何映射键到特定的段上。

总结

  1. 安全性:ConcurrentHashMap 在多线程环境下是安全的,而HashMap 不是。
  2. 使用场景:如果是单线程环境,使用HashMap 更优;如果是多线程环境,且需要高性能的线程安全Map,则应选择ConcurrentHashMap。
  3. 底层原理:ConcurrentHashMap 通过分段锁(JDK1.7)或更细粒度的锁(JDK1.8)来实现线程安全

参考

https://juejin.cn/post/7361629576416247849

相关推荐
努力学习的小廉20 分钟前
【QT(七)】—— 常用控件(四)
开发语言·qt
Coder_Boy_26 分钟前
基于SpringAI的在线考试系统-DDD(领域驱动设计)核心概念及落地架构全总结(含事件驱动协同逻辑)
java·人工智能·spring boot·微服务·架构·事件驱动·领域驱动
CoderCodingNo26 分钟前
【GESP】C++六级考试大纲知识点梳理, (3) 哈夫曼编码与格雷码
开发语言·数据结构·c++
froginwe1135 分钟前
C 标准库 - `<errno.h>`
开发语言
黎雁·泠崖39 分钟前
Java&C语法对比:分支与循环结构核心全解析
java·c语言
鹿角片ljp1 小时前
Java IO流案例:使用缓冲流恢复《出师表》文章顺序
java·开发语言·windows
毕设源码-郭学长1 小时前
【开题答辩全过程】以 广告投放管理系统为例,包含答辩的问题和答案
java
纵有疾風起1 小时前
【Linux 系统开发】基础开发工具详解:自动化构建、版本控制与调试器开发实战
linux·服务器·开发语言·c++·经验分享·开源·bash
小北方城市网1 小时前
SpringBoot 集成 RabbitMQ 实战(消息队列解耦与削峰):实现高可靠异步通信
java·spring boot·python·微服务·rabbitmq·java-rabbitmq·数据库架构
java_t_t1 小时前
Maven插件apiscan介绍与使用
java·maven·api文档·maven插件