ConcurrentHashMap之核心方法分析(JDK1.8)

ConcurrentHashMap是java.util.concurrent并发包下的一个类,相比jdk1.7,jdk1.8使用了cas来替代分段锁,其和HashMap一样底层使用了数组+链表+红黑树。

字段

 private static final int MAXIMUM_CAPACITY = 1 << 30;//最大容量 2^30
    private static final int DEFAULT_CAPACITY = 16; //默认容量 2^4
    static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;//最大数组大小
    private static final int DEFAULT_CONCURRENCY_LEVEL = 16;//jdk1.8没有使用,为了和上个版本兼容
    private static final float LOAD_FACTOR = 0.75f;//加载因子
    static final int TREEIFY_THRESHOLD = 8; //树化阈值
    static final int UNTREEIFY_THRESHOLD = 6; //树转链表阈值
    static final int MIN_TREEIFY_CAPACITY = 64;//最小树化容量
    private static final int MIN_TRANSFER_STRIDE = 16;
    private static int RESIZE_STAMP_BITS = 16;//resizeStamp(n)生成戳的位数
    private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;//最大帮助扩容线程数
    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
 /**
     * 当sizeCt=-1时表示正在初始化
     * 当sizeCtl<-1时,表示扩容的线程数量为|sizeCtl|-1
     * 当sizeCtl>=0时,表示下次扩容的阈值
     */
    private transient volatile int sizeCtl;

在看putVal()方法之前,先来了解一下table的初始化方法

 private final Node<K,V>[] initTable() {
        Node<K,V>[] tab; int sc;
        //确认其他线程未对table修改
        while ((tab = table) == null || tab.length == 0) {
            //如果有其他线程在进行初始化或者扩容,则让出线程
            if ((sc = sizeCtl) < 0)
                Thread.yield(); // lost initialization race; just spin
            //否则cas修改sizeCtl为-1,表示正在进行初始化操作
            else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                try {
                    //再次确认table还未被其他线程修改(可能在cas竞争到锁后,其他线程已经对table完成修改)
                    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 = 0.75 * n
                        sc = n - (n >>> 2);
                    }
                } finally {
                    sizeCtl = sc;
                }
                break;
            }
        }
        return tab;
    }

ConcurrentHashMap的put()方法和HashMap的实现思想都差不多,我们直接来看putVal()方法

 final V putVal(K key, V value, boolean onlyIfAbsent) {
        //如果key/value为空,抛出空指针异常  ConcurrentHashMap不允许键值对象为null
        if (key == null || value == null) throw new NullPointerException();
        //计算hash值
        int hash = spread(key.hashCode());
        int binCount = 0;
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            //如果table没有进行初始化,则先进行初始化操作
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();
            //(n - 1) & hash) 相当于 hash%n即table的索引,tabAt()获取table的指定索引的节点
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                //如果节点为空,那么直接插入,casTabAt()将节点插入指定位置
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }
            //如果节点正在移动,那么帮助其扩容
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);
            else {
                V oldVal = null;
                synchronized (f) {
                    if (tabAt(tab, i) == f) {
                        if (fh >= 0) {
                            binCount = 1;
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                                //如果key相同,则更新值
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    oldVal = e.val;
                                    if (!onlyIfAbsent)
                                        e.val = value;
                                    break;
                                }
                                Node<K,V> pred = e;
                                //否则插入链尾
                                if ((e = e.next) == null) {
                                    pred.next = new Node<K,V>(hash, key,
                                                              value, null);
                                    break;
                                }
                            }
                        }
                        //ConcurrentHashMap 树节点为TreeBin,其封装了TreeNode
                        else if (f instanceof TreeBin) {
                            Node<K,V> p;
                            binCount = 2;
                            //插入树节点
                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                           value)) != null) {
                                oldVal = p.val;
                                if (!onlyIfAbsent)
                                    p.val = value;
                            }
                        }
                    }
                }
                if (binCount != 0) {
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        addCount(1L, binCount);
        return null;
    }

get()方法类似,

public V get(Object key) {
        Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
        //计算hash
        int h = spread(key.hashCode());
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (e = tabAt(tab, (n - 1) & h)) != null) {
            if ((eh = e.hash) == h) {
                //如果key相同,则返回value
                if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                    return e.val;
            }
            //如果小于0则为特殊节点,使用find方法寻找
            else if (eh < 0)
                return (p = e.find(h, key)) != null ? p.val : null;
            while ((e = e.next) != null) {
                if (e.hash == h &&
                    ((ek = e.key) == key || (ek != null && key.equals(ek))))
                    return e.val;
            }
        }
        return null;
    }

tryPreSize()方法和resizeStamp()我的一篇博客中讲过ConcurrentHashMap源码之tryPreSize 

final V replaceNode(Object key, V value, Object cv) {
        //计算hash
        int hash = spread(key.hashCode());
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            //表为空则返回
            if (tab == null || (n = tab.length) == 0 ||
                (f = tabAt(tab, i = (n - 1) & hash)) == null)
                break;
            //如果节点正在移动,则帮助其移动
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);
            else {
                V oldVal = null;
                boolean validated = false;
                synchronized (f) {
                    //即检查table是否修改
                    if (tabAt(tab, i) == f) {
                        if (fh >= 0) {
                            validated = true;
                            for (Node<K,V> e = f, pred = null;;) {
                                K ek;
                                //找到key,然后修改指针,使其孤立
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    V ev = e.val;
                                    if (cv == null || cv == ev ||
                                        (ev != null && cv.equals(ev))) {
                                        oldVal = ev;
                                        if (value != null)
                                            e.val = value;
                                        else if (pred != null)
                                            pred.next = e.next;
                                        else
                                            setTabAt(tab, i, e.next);
                                    }
                                    break;
                                }
                                pred = e;
                                if ((e = e.next) == null)
                                    break;
                            }
                        }
                        //如果为树,很简单的实现
                        else if (f instanceof TreeBin) {
                            validated = true;
                            TreeBin<K,V> t = (TreeBin<K,V>)f;
                            TreeNode<K,V> r, p;
                            if ((r = t.root) != null &&
                                (p = r.findTreeNode(hash, key, null)) != null) {
                                V pv = p.val;
                                if (cv == null || cv == pv ||
                                    (pv != null && cv.equals(pv))) {
                                    oldVal = pv;
                                    if (value != null)
                                        p.val = value;
                                    else if (t.removeTreeNode(p))
                                        setTabAt(tab, i, untreeify(t.first));
                                }
                            }
                        }
                    }
                }
                if (validated) {
                    if (oldVal != null) {
                        if (value == null)
                            addCount(-1L, -1);
                        return oldVal;
                    }
                    break;
                }
            }
        }
        return null;
    }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值