ConcurrentHashMap(jdk1.7)源码浅读

本文深入解读了JDK1.7中ConcurrentHashMap的源码,详细介绍了其“数组+链表”的数据结构,以及通过分段锁(Segment继承ReentrantLock)实现线程安全的方法。在数据存储上,每个Segment包含一个HashEntry数组,形成链表。put和get方法的实现逻辑也进行了说明,突出了分段加锁提高并发性能的设计思想。
摘要由CSDN通过智能技术生成

一、简介

ConcurrentHashMap是jdk下current包中线程安全的key/value集合。这里对jdk1.7中ConcurrentHashMap的

源码进行解读。主要包含实现了ReentrantLock锁的Segment类及其数组,HashEntry类及其数组。

二、ConcrrentHashMap基本数据结构

1、jdk1.7中的ConcrrentHashMap基本数据结构为“数组+链表”。

2、为了线程安全,将数据分段加锁(分段为segment,其直接继承了ReentrantLock锁),各个分段构成

一个数组,每个分段里是HashEntry数组,每个HashEntry是数据的存储节点,同时也是链表节点。

采用分段加锁(使用ReentrantLock锁),使多个线程可同时操作ConcurrentHashMap的各个段,

ReentrantLock锁使锁操作更灵活,更高效,在加锁前,先尝试获取锁。

3、数据结构示例如下:

segment[0](ReentrantLock锁) -- HashEntry[0]  --> HashEntry --> HashEntry
                               HashEntry[1]  --> HashEntry
                               HashEntry[2]  --> HashEntry --> HashEntry --> HashEntry
                               .
                               .

segment[1](ReentrantLock锁) -- HashEntry[0]  --> HashEntry --> HashEntry
                               HashEntry[1]  --> HashEntry
                               HashEntry[2]  --> HashEntry --> HashEntry --> HashEntry
                               .
                               .

segment[2](ReentrantLock锁) -- HashEntry[0]  --> HashEntry --> HashEntry
                               HashEntry[1]  --> HashEntry
                               HashEntry[2]  --> HashEntry --> HashEntry --> HashEntry
                               .
                               .
.
.
.

三、源码解读

jdk1.7中ConcurrentHashMap最大的特点是采用了分段锁。

1、ConcurrentHashMapextends继承了AbstractMap抽象类,实现了ConcurrentMap接口。

源码如下:

public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
        implements ConcurrentMap<K, V>, Serializable {
 

2、ConcurrentHashMap是分段加锁,每个段为Segment类,其继承了ReentrantLock类,

段内包含HashEntry数组(真正存储数据元素的地方),

Segment类定义部分源码如下:

static final class Segment<K,V> extends ReentrantLock implements Serializable {
    /*
     * Segments maintain a table of entry lists that are always
     * kept in a consistent state, so can be read (via volatile
     * reads of segments and tables) without locking.  This
     * requires replicating nodes when necessary during table
     * resizing, so the old lists can be traversed by readers
     * still using old version of table.
     *
     * This class defines only mutative methods requiring locking.
     * Except as noted, the methods of this class perform the
     * per-segment versions of ConcurrentHashMap methods.  (Other
     * methods are integrated directly into ConcurrentHashMap
     * methods.) These mutative methods use a form of controlled
     * spinning on contention via methods scanAndLock and
     * scanAndLockForPut. These intersperse tryLocks with
     * traversals to locate nodes.  The main benefit is to absorb
     * cache misses (which are very common for hash tables) while
     * obtaining locks so that traversal is faster once
     * acquired. We do not actually use the found nodes since they
     * must be re-acquired under lock anyway to ensure sequential
     * consistency of updates (and in any case may be undetectably
     * stale), but they will normally be much faster to re-locate.
     * Also, scanAndLockForPut speculatively creates a fresh node
     * to use in put if no node is found.
     */

    private static final long serialVersionUID = 2249069246763182397L;

    /**
     * The maximum number of times to tryLock in a prescan before
     * possibly blocking on acquire in preparation for a locked
     * segment operation. On multiprocessors, using a bounded
     * number of retries maintains cache acquired while locating
     * nodes.
     */
    static final int MAX_SCAN_RETRIES =
        Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1;

    /**
     * The per-segment table. Elements are accessed via
     * entryAt/setEntryAt providing volatile semantics.
     */
    transient volatile HashEntry<K,V>[] table;

    /**
     * The number of elements. Accessed only either within locks
     * or among other volatile reads that maintain visibility.
     */
    transient int count;

    /**
     * The total number of mutative operations in this segment.
     * Even though this may overflows 32 bits, it provides
     * sufficient accuracy for stability checks in CHM isEmpty()
     * and size() methods.  Accessed only either within locks or
     * among other volatile reads that maintain visibility.
     */
    transient int modCount;

    /**
     * The table is rehashed when its size exceeds this threshold.
     * (The value of this field is always <tt>(int)(capacity *
     * loadFactor)</tt>.)
     */
    transient int threshold;

    /**
     * The load factor for the hash table.  Even though this value
     * is same for all segments, it is replicated to avoid needing
     * links to outer object.
     * @serial
     */
    final float loadFactor;

    Segment(float lf, int threshold, HashEntry<K,V>[] tab) {
        this.loadFactor = lf;
        this.threshold = threshold;
        this.table = tab;
    }

段内元素更新操作时,都必须先tryLock()尝试获取锁,之后才能进行更新操作,关键方法是:

scanAndLock(Object key, int hash)和scanAndLockForPut(K key, int hash, V value),

段内元素更新操作部分源码如下:

final V put(K key, int hash, V value, boolean onlyIf
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. Segment 数量 在 JDK1.7ConcurrentHashMap 内部的 Segment 数组的长度是固定的,由一个常量指定,即 16。每个 Segment 可以被看作是一个小的 ConcurrentHashMap。在 JDK1.8 中,Segment 被取消了,取而代之的是一个名为 Node 的数组,这个数组的长度是可变的,它的长度被初始化为 2 的幂次方,具体的大小取决于当前 ConcurrentHashMap 中元素的数量。 2. 数据结构 在 JDK1.7ConcurrentHashMap 内部的每个 Segment 由一个 HashEntry 数组和一个 HashEntry 链表组成。当多个线程同时访问 ConcurrentHashMap 时,它们只会锁定对应的 Segment,而不是整个 ConcurrentHashMap。在 JDK1.8 中,每个 Node 是一个单独的元素,它可以包含多个 key-value 对,每个 Node 之间通过链表进行关联。 3. 锁的优化 在 JDK1.8 中,ConcurrentHashMap 中使用了 CAS 操作来进行锁的优化。在 JDK1.7 中,ConcurrentHashMap 中需要使用锁来保证线程安全,这个锁是对每个 Segment 进行的,也就是说,在多线程访问 ConcurrentHashMap 时,每个线程只能同时访问不同的 Segment,这样可以避免锁的竞争,提高了并发性能。 4. 数据存储方式 在 JDK1.7ConcurrentHashMap 中的数据存储方式是数组和链表结合的方式,其中数组是用来存储数据的,链表是用来解决哈希冲突的。而在 JDK1.8 中 ConcurrentHashMap 中的数据存储方式是数组和链表以及红黑树结合的方式,当链表长度大于 8 时,链表会自动转化为红黑树,这样可以提高查找效率。 5. 性能 由于 JDK1.8 中 ConcurrentHashMap 中使用了 CAS 操作来进行锁的优化,因此在并发性能方面有了很大的提升。同时,在数据存储方式方面,JDK1.8 中 ConcurrentHashMap 中使用了红黑树来优化哈希冲突,这样可以提高查找效率。因此,JDK1.8 中 ConcurrentHashMap 的性能比 JDK1.7ConcurrentHashMap 更加出色。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值