zoukankan      html  css  js  c++  java
  • 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

    原子操作

    静态方法

    四 总结

  • 相关阅读:
    HTTP概述
    【HTTP权威指南】第二章URL与资源
    【HTTP权威指南】第三章HTTP报文
    列表生成式
    六一问候
    NYOJ 528 找球号(三)
    NYOJ 138 找球号(二)
    HDU3790 最短路径问题
    NYOJ 228 士兵杀敌(五)
    NYOJ3 3 多边形重心问题
  • 原文地址:https://www.cnblogs.com/houzheng/p/12693774.html
Copyright © 2011-2022 走看看