JDK源码之ConcurrentHashMap类分析

一 概述

jdk1.8之前

在JDK1.6中,ConcurrentHashMap将数据分成一段一段存储,给每一段数据配一把锁,当一个线程获得锁互斥访问一个段数据时,其他段的数据也可被其他线程访问;
每个Segment拥有一把可重入锁,因此ConcurrentHashMap的分段锁数目即为Segment数组长度。
ConcurrentHashMap结构:每一个segment都是一个HashEntry<K,V>[] table, table中的每一个元素本质上都是一个HashEntry的单向队列(单向链表实现)。
每一个segment都是一个HashEntry<K,V>[] table, table中的每一个元素本质上都是一个HashEntry的单向队列

jdk1.8之后

在jdk1.8以上已经抛弃了Segment的概念,底层数据结构与HashMap相同,仍然采用table数组+链表+红黑树结构;
一个线程进行put/remove操作时,对桶(链表 or 红黑树)加上synchronized独占锁;ConcurrentHashMap采用CAS算法保证线程安全;
这里只讨论分析jdk1.8的源代码

二 ConcurrentMap接口

ConcurrentHashMap实现了ConcurrentMap接口,是一种提供线程安全性和原子性保证的Map.之前HashMap中已经分析过抽象map接口,这里简单分析下此接口:

抽象方法

        // 这四个方法在Map<K,V>接口已经定义,并且都是default方法。
        // 在ConcurrentMap中因为这些方法需要保证多线程下的原子性,
        // 因此在ConcurrentMap中都是abstract方法,需要子类实现,保证线程安全。
        V putIfAbsent(K key, V value);

        boolean remove(Object key, Object value);

        boolean replace(K key, V oldValue, V newValue);

        V replace(K key, V value);
        // 其他还有些覆写了Map接口的部分default方法,实现稍微会有点区别

三 源码分析

ConcurrentHashMap里面有大部分常量和底层结构方法都跟HashMap相同,这里不全分析,只重点分析跟HashMap不同的地方以及实现原理:

属性

            // 默认并发线程数
            private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
            // 每次进行转移的最小值
            private static final int MIN_TRANSFER_STRIDE = 16;
            // 生成sizeCtl所使用的bit位数
            private static final int RESIZE_STAMP_BITS = 16;
            // 进行扩容所允许的最大线程数
            private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
            // 记录sizeCtl中的大小所需要进行的偏移位数
            private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
            // 一系列的标识
            static final int MOVED     = -1; // hash for forwarding nodes
            static final int TREEBIN   = -2; // hash for roots of trees
            static final int RESERVED  = -3; // hash for transient reservations
            static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash
            // 获取可用的CPU个数
            static final int NCPU = Runtime.getRuntime().availableProcessors();
            //table数组
            transient volatile Node<K,V>[] table;
            // rehash扩容时用到的新键值对数组
            private transient volatile Node<K,V>[] nextTable;
            // 记录当前键值对总数,通过CAS更新,对所有线程可见
            private transient volatile long baseCount;
            /**
             * sizeCtl表示键值对总数阈值,通过CAS更新, 对所有线程可见
             * 当sizeCtl < 0时,表示多个线程在等待扩容;
             * 当sizeCtl = 0时,默认值;
             * 当sizeCtl > 0时,表示扩容的阈值;
             */
            private transient volatile int sizeCtl;
            // 扩容下一个表的索引
            private transient volatile int transferIndex;
            // 自旋锁
            private transient volatile int cellsBusy;
            // counterCell表
            private transient volatile CounterCell[] counterCells;
            // 视图
            private transient KeySetView<K,V> keySet;
            private transient ValuesView<K,V> values;
            private transient EntrySetView<K,V> entrySet;

Node静态内部类

vaue值和next用volatile修饰,不支持setValue,提供find方法,其他和HashMap中Node一样:

            static class Node<K,V> implements Map.Entry<K,V> {
                final int hash;
                final K key;
                volatile V val; // val和next都使用volatile修饰
                volatile Node<K,V> next;
                // ..... 构造方法和get/setKey(),hashCode等方法
                // 不支持setValue
                public final V setValue(V value) {
                    throw new UnsupportedOperationException();
                }
                // 提供查找方法,调用节点只能是哈希桶链表头节点
                Node<K,V> find(int h, Object k) {
                    Node<K,V> e = this;
                    if (k != null) {
                        do {
                            K ek;
                            if (e.hash == h &&
                                    ((ek = e.key) == k || (ek != null && k.equals(ek))))
                                return e;
                        } while ((e = e.next) != null);
                    }
                    return null;
                }
            }

构造函数

put

get

remove

原子操作

静态方法

四 总结

posted @ 2020-04-13 20:33  侯小厨  阅读(209)  评论(0编辑  收藏  举报
Fork me on Gitee