ConcurrentHashMap源码解析(JDK1.8)

目录

ConcurrentHashMap源码解析

一、什么是ConcurrentHashMap

二、ConcurrentHashMap锁的颗粒度

三、源码解析

1.构造函数及部分参数

2.初始化

3.put操作

4.get操作

5.remove操作

6.clear操作



ConcurrentHashMap源码解析

一、什么是ConcurrentHashMap

ConcurrentHashMap与HashMap数据结构一致,他是线程安全的。

ConcurrentHashMap中是不允许key和value为null的,而HashMap是允许的。

ConcurrentHashMap的扩容是可以多线程扩容的,桶中节点数量可以多线程计数。

数据结构详情:HashMap源码解析

 

jdk1.7和1.8是如何对并发做处理的?

jdk1.7: 使用了一个继承ReentrantLock的分段锁Segment来实现并发的

jdk1.8:是通过CAS + synchronized + volatile 的方式来实现,而且锁粒度也缩减到了桶上。

CAS详情:什么是CAS

synchronized详情:全面理解Synchronized

volatile:全面理解Volatile关键字

 

 

二、ConcurrentHashMap锁的颗粒度

jdk1.8锁的颗粒度在桶上:通过数据结构我们可以看到,底层采用的是数组+链表+红黑树(在一定的情况下才会有红黑树)。这个桶就是数组上的那个链表。ConcurrentHashMap并发时加锁是加在这个桶中的第一个node节点,也就是对整个桶进行了加锁。这样的话,若是有2个线程同时对ConcurrentHashMap进行put操作,若是线程1对Node[1]桶里的数据进行更新,线程2对Node[3]桶中的数据进行更新,那么就可以同时进行,不会被阻塞。

 

 

三、源码解析

1.构造函数及部分参数

//正在扩容
static final int MOVED = -1; // hash for forwarding nodes
//当前节点是红黑树节点
static final int TREEBIN = -2; // hash for roots of trees
//当前节点是用在computeIfAbsent和compute方法中的占位节点
static final int RESERVED = -3; // hash for transient reservations

//map的数据结构,通过volatile修饰的
transient volatile Node<K, V>[] table;

//扩容后的新table
private transient volatile Node<K, V>[] nextTable;

//桶中node的计数器
private transient volatile long baseCount;

//初始化或扩容的标识,用volatile修饰的
private transient volatile int sizeCtl;

//调整大小时要拆分的下一个表索引
private transient volatile int transferIndex;
//构造函数,初始化数组大小
public ConcurrentHashMap(int initialCapacity) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException();
    int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
            MAXIMUM_CAPACITY :
            tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
    this.sizeCtl = cap;
}

//构造函数,将一个map集合全部赋值
public ConcurrentHashMap(Map<? extends K, ? extends V> m) {
    this.sizeCtl = DEFAULT_CAPACITY;
    putAll(m);
}

public ConcurrentHashMap(int initialCapacity,
                         float loadFactor, int concurrencyLevel) {
    if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
        throw new IllegalArgumentException();
    if (initialCapacity < concurrencyLevel)   // Use at least as many bins
        initialCapacity = concurrencyLevel;   // as estimated threads
    long size = (long) (1.0 + (long) initialCapacity / loadFactor);
    int cap = (size >= (long) MAXIMUM_CAPACITY) ?
            MAXIMUM_CAPACITY : tableSizeFor((int) size);
    this.sizeCtl = cap;
}

2.初始化

//初始化table数组
private final Node<K, V>[] initTable() {
    Node<K, V>[] tab;
    int sc;
    while ((tab = table) == null || tab.length == 0) {
        if ((sc = sizeCtl) < 0)
            //sizeCtl值为-1时,表示正在初始化,因为sizeCtl是被volatile修饰的,所以一旦有一个线程在初始化,其他线程立马能感知到,那么就让出当前线程资源,没必要在这卡着,因为已经有线程在初始化了
            Thread.yield(); // lost initialization race; just spin
        else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
            //通过CAS将sizeCtl改为-1,并且已经获取到资源,能够进入if逻辑中
            //给map的数组初始化
            try {
                if ((tab = table) == null || tab.length == 0) {
                    int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                    @SuppressWarnings("unchecked")
                    Node<K, V>[] nt = (Node<K, V>[]) new Node<?, ?>[n];
                    table = tab = nt;
                    sc = n - (n >>> 2);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值