ConcurrentHashMap源码之put和get方法

本文分析了JDK1.7中ConcurrentHashMap的实现原理,重点讲解了put和get方法。在put操作中,通过锁分段技术,首先计算key的hash值确定segment,再通过unsafe类获取并初始化segment,最后在segment上加锁并更新链表。get操作则利用volatile确保获取最新值,无需加锁。同时,文章提到了tryLock和scanAndLockForPut方法,以及HashEntry节点的volatile特性。
摘要由CSDN通过智能技术生成

以下ConcurrentHashMap类是基于jdk1.7来分析。

JDK1.7中ConcurrentHashMap是通过segments数组和HashEntry数组+链表来进行实现的。利用锁分段技术,支持任务数量线程的读和一定数量线程的写。

我们看下ConcurrentHashMap是怎么进行put和get操作的。

1、ConcurrentHashMap的put方法不能插入null值(为什么?自行百度),在put kv值时,首先取key的hash值,通过hash值判断key所在的segment,然后使用unsafe类的本地方法获取此segments数组中hash值对应的segment是否为null(为什么用unsafe类呢?因为需要获取内存中最新的存储值,关于unsafe类直接操作内存,参考这里。),如果为null,则初始化segment元素,然后调用segment的put方法。ConcurrentHashMap类的put方法源码如下

//ConcurrentHashMap的put方法
public V put(K key, V value) {
        Segment<K,V> s;
//value值为null,直接报异常
        if (value == null)
            throw new NullPointerException();
//两次hash,获得key的哈希值
        int hash = hash(key);
//对hash值的高位和segmentMask掩码做按位与,确定key所在的segment(segmentMask=segment的长度-1)
        int j = (hash >>> segmentShift) & segmentMask;
//通过Unsafe类获取segments数组中下标为j的元素,如果不存在就初始化segment。(SSHIFT和SBASE均为确定数组元素的内存位置,见以下变量声明和static块初始化)
        if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck
             (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment
            s = ensureSegment(j);
        return s.put(key, hash, value, false);
    }

// Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    private static final long SBASE;
    private static final int SSHIFT;
    private static final long TBASE;
    private static final int TSHIFT;
    private static final long HASHSEED_OFFSET;
    private static final long SEGSHIFT_OFFSET;
    private static final long SEGMASK_OFFSET;
    private static final long SEGMENTS_OFFSET;
    static {
        int ss, ts;
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class tc = HashEntry[].class;
            Class sc = Segment[].class;
            TBASE = UNSAFE.arrayBaseOffset(tc);
            SBASE = UNSAFE.arrayBaseOffset(sc);
            ts = UNSAFE.arrayIndexScale(tc);
            ss = UNSAFE.arrayIndexScale(sc);
            HASHSEED_OFFSET = UNSAFE.objectFieldOffset(
                ConcurrentHashMap.class.getDeclaredField("hashSeed"));
            SEGSHIFT_OFFSET
ConcurrentHashMapJava中线程安全的哈希表实现。它是通过使用分段锁(Segment)来实现并发访问的。 在ConcurrentHashMap源码中,主要有以下几个核心类和接口: 1. ConcurrentHashMap类:ConcurrentHashMap类是ConcurrentMap接口的实现类,它继承自AbstractMap类。它提供了线程安全的并发访问和更新操作ConcurrentHashMap内部由一个Segment数组组成,每个Segment都是一个独立的哈希表,它们之间互不影响。 2. Segment类:Segment类是ConcurrentHashMap的内部类,每个Segment对象都相当于一个独立的哈希表。Segment内部使用ReentrantLock来实现分段锁,不同的Segment之间可以并发访问。 3. HashEntry类:HashEntry类是ConcurrentHashMap中存储键值对的节点对象,它包含了键、值和下一个节点的引用。ConcurrentHashMap使用链表来解决哈希冲突,每个Segment中都维护了一个HashEntry数组,数组的每个元素都是一个链表的头节点。 4. ConcurrentHashMap的核心方法ConcurrentHashMap提供了一系列线程安全的操作方法,包括put、get、remove等。这些方法会根据键的哈希值选择对应的Segment,并在该Segment上加锁进行操作。 总体来说,ConcurrentHashMap通过将整个哈希表分成多个Segment,每个Segment都是一个独立的哈希表,并使用分段锁来实现对不同Segment的并发访问。这种设计在多线程环境下能够提供较好的性能和并发能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值