8 ConcurrentHashMap 源码注释2

/**
     * 如果指定的值key还没有关联到一个value,那么尝试使用给定的函数计算它的值,
     * 如果值不为null,则将它放入此map中
     * If the specified key is not already associated with a value,
     * attempts to compute its value using the given mapping function
     * and enters it into this map unless {@code null}.  The entire
     * 整个方法调用是原子执行的,因此这个函数每个key最多使用一次。
     * method invocation is performed atomically, so the function is
     * applied at most once per key.  Some attempted update operations
     * 在计算过程中可能会阻止其他线程对该map进行的某些尝试更新操作,因此计算应该简短并且简单,
     * 不得尝试更新此map的任何其他映射。
     * on this map by other threads may be blocked while computation
     * is in progress, so the computation should be short and simple,
     * and must not attempt to update any other mappings of this map.
     *
     * @param key key with which the specified value is to be associated
     * @param mappingFunction the function to compute a value
     * @return the current (existing or computed) value associated with
     *         the specified key, or null if the computed value is null
     * @throws NullPointerException if the specified key or mappingFunction
     *         is null
     * @throws IllegalStateException if the computation detectably
     *         attempts a recursive update to this map that would
     *         otherwise never complete
     * @throws RuntimeException or Error if the mappingFunction does so,
     *         in which case the mapping is left unestablished
     */
    public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
        // 判空
        if (key == null || mappingFunction == null)
            throw new NullPointerException();

        // 计算key 的哈希值
        int h = spread(key.hashCode());
        V val = null;
        int binCount = 0;
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            if (tab == null || (n = tab.length) == 0)
                // 如果table还没初始化,那么初始化table
                tab = initTable();

            // 如果key 对应的索引位置上还没有元素
            else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
                // todo : 为什么不使用CAS把值放入
                //  -->不直接使用CAS把目标node放入的原因是:使用给定函数计算目标值可能是一个比较耗时的过程,一旦发现该
                //     索引位置上没有元素,那么先创建一个ReservationNode,并使用它作为该索引位置的锁,然后获取锁,再使
                //     用CAS设置进去,占住该索引位置,然后再利用给定的函数,计算其值,计算完成了再创建目标node,然后替换
                //     为目标node。否则CAS失败了还得判断是否存在这个元素,然后继续处理
                Node<K,V> r = new ReservationNode<K,V>();

                // 先获取锁,再使用CAS设置值,避免CAS成功后,被其他线程先获取到了锁
                synchronized (r) {
                    if (casTabAt(tab, i, null, r)) {
                        // CAS成功后,binCount值设置为1
                        binCount = 1;
                        Node<K,V> node = null;
                        try {
                            // 使用给定的函数计算值,参数传入key
                            if ((val = mappingFunction.apply(key)) != null)
                                node = new Node<K,V>(h, key, val, null);

                            // 如果计算的值为null,那么什么都不做
                        } finally {
                            // 1、如果异常了,那么会把该索引位置上的值设置回null
                            // 2、如果没有异常,那么会把该索引位置上的值设置为目标node
                            // ReservationNode 会被覆盖掉
                            setTabAt(tab, i, node);
                        }
                    }
                }
                // binCount != 0,说明数据插入完成了,也可能值为null,没有进行插入,
                // 如果是计算出现异常,那么线程不会执行到这里,上面就抛出异常了
                if (binCount != 0)
                    break;
            }

            // 如果当前正在进行扩容,那么先帮忙搬运数据
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);

            else {
                boolean added = false;
                // 先获取锁
                synchronized (f) {
                    // 获取到锁后还需要判断,该索引位置的第一个元素是否还是f
                    if (tabAt(tab, i) == f) {
                        if (fh >= 0) {
                            binCount = 1;
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek; V ev;
                                if (e.hash == h &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    // key 已存在,获取key 映射的值
                                    val = e.val;
                                    break;  // 结束循环
                                }
                                Node<K,V> pred = e;
                                // e = e.next, e.next = null说明该索引位置上没有其他元素了
                                if ((e = e.next) == null) {
                                    // 使用给定的函数计算值,参数传入key
                                    if ((val = mappingFunction.apply(key)) != null) {
                                        added = true;
                                        // 插入数据
                                        pred.next = new Node<K,V>(h, key, val, null);
                                    }
                                    // 如果计算的值为null,那么什么都不做

                                    // break 退出循环,binCount不会再自增
                                    break;
                                }
                            }
                        }
                        else if (f instanceof TreeBin) {
                            // binCount 设置为2,当存在竞争的时候可以进行扩容,当存在多线程竞争的时候 binCount <= 1,不会
                            // 进行扩容
                            binCount = 2;
                            TreeBin<K,V> t = (TreeBin<K,V>)f;
                            TreeNode<K,V> r, p;
                            // findTreeNode()查找key对应的节点,没有返回null
                            // 如果 p != null,说明key 已经存在
                            if ((r = t.root) != null &&
                                (p = r.findTreeNode(h, key, null)) != null)
                                // 获取key 映射的值
                                val = p.val;
                            // 如果key不存在,使用给定的函数计算值,参数传入key
                            else if ((val = mappingFunction.apply(key)) != null) {
                                added = true;
                                t.putTreeVal(h, key, val);
                            }
                        }
                    }
                }
                // 如果该索引位置在插入之前已有元素,那么binCount 一定不为0,但是 tabAt(tab, i) == f值为false的
                // 时候binCount的值为0,而added的值是false,所以这里必须要判断 binCount != 0
                if (binCount != 0) {
                    // TREEIFY_THRESHOLD = 8。 binCount不包含新插入的元素,因此加上新插入的元素,slot中元素个数达到
                    // 9个才会转成红黑树。HashMap的computeIfAbsent()元素个数达到8个就转成红黑树
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    // added = false 说明key在之前已经存在,直接返回原来的值,元素个数不需要 +1
                    if (!added)
                        return val;
                    break;
                }
            }
        }

        if (val != null)
            // 元素个数 + 1
            addCount(1L, binCount);

        // 返回原来的值/新的值
        return val;
    }

    /**
     * If the value for the specified key is present, attempts to
     * compute a new mapping given the key and its current mapped
     * value.  The entire method invocation is performed atomically.
     * Some attempted update operations on this map by other threads
     * may be blocked while computation is in progress, so the
     * computation should be short and simple, and must not attempt to
     * update any other mappings of this map.
     *
     * @param key key with which a value may be associated
     * @param remappingFunction the function to compute a value
     * @return the new value associated with the specified key, or null if none
     * @throws NullPointerException if the specified key or remappingFunction
     *         is null
     * @throws IllegalStateException if the computation detectably
     *         attempts a recursive update to this map that would
     *         otherwise never complete
     * @throws RuntimeException or Error if the remappingFunction does so,
     *         in which case the mapping is unchanged
     */
    public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        // 判空
        if (key == null || remappingFunction == null)
            throw new NullPointerException();
        // 计算key 的hash值
        int h = spread(key.hashCode());
        V val = null;
        int delta = 0;
        int binCount = 0;
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            if (tab == null || (n = tab.length) == 0)
                // 如果table还没初始化,那么初始化table
                tab = initTable();

            // 如果该索引位置上还没有元素,那么结束循环
            else if ((f = tabAt(tab, i = (n - 1) & h)) == null)
                break;

                // 如果当前正在进行扩容,那么先帮忙搬运数据
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);

            else {
                synchronized (f) {
                    if (tabAt(tab, i) == f) {
                        if (fh >= 0) {
                            binCount = 1;
                            for (Node<K,V> e = f, pred = null;; ++binCount) {
                                K ek;
                                if (e.hash == h &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    // 找到key 所在的节点,使用给定的函数计算值,参数传入 key,原来的值
                                    val = remappingFunction.apply(key, e.val);

                                    if (val != null)
                                        // 如果值不为null,替换原来的值
                                        e.val = val;
                                    else {
                                        // 如果计算出新的值为null,那么删除这个节点
                                        delta = -1;
                                        // Node 只有next,没有prev
                                        Node<K,V> en = e.next;
                                        if (pred != null)
                                            // pred.next 指向删除节点的下一个节点
                                            pred.next = en;
                                        else
                                            // 如果删除的节点是该索引位置上的第一个节点,那么修改
                                            // 第一个节点为: e.next
                                            setTabAt(tab, i, en);
                                    }
                                    break;
                                }
                                pred = e;

                                // 该索引位置上没有下一个元素了,break
                                if ((e = e.next) == null)
                                    break;
                            }
                        }
                        else if (f instanceof TreeBin) {
                            // binCount 设置为2,当存在竞争的时候可以进行扩容,当存在多线程竞争的时候 binCount <= 1,不会
                            // 进行扩容。 在这里的主要作用就是下面的判断: if (binCount != 0)  break;
                            binCount = 2;
                            TreeBin<K,V> t = (TreeBin<K,V>)f;
                            TreeNode<K,V> r, p;
                            if ((r = t.root) != null &&
                                (p = r.findTreeNode(h, key, null)) != null) {
                                // p != null 说明 key已存在此map中

                                // 使用给定的函数计算新的值,参数传入 key,原来的值
                                // 这里把原来的值传进去了,因此可以很容易地实现在原来的值上做加减
                                val = remappingFunction.apply(key, p.val);

                                if (val != null)
                                    // 如果新的值不为null,那么替换原来的值
                                    p.val = val;
                                else {
                                    // 如果新的值为null,那么删除此节点
                                    delta = -1;
                                    // removeTreeNode()如果删除后红黑树的结构太小,如果返回true,表示需要把红黑树转成链表
                                    if (t.removeTreeNode(p))
                                        setTabAt(tab, i, untreeify(t.first));
                                }
                            }
                        }
                    }
                }
                if (binCount != 0)
                    break;
            }
        }

        // delta = -1,说明新的值为null,那么会把key所在的节点删除,因此元素个数 -1
        if (delta != 0)
            addCount((long)delta, binCount);
        // 返回新的值
        return val;
    }

    /**
     * Attempts to compute a mapping for the specified key and its
     * current mapped value (or {@code null} if there is no current
     * mapping). The entire method invocation is performed atomically.
     * Some attempted update operations on this map by other threads
     * may be blocked while computation is in progress, so the
     * computation should be short and simple, and must not attempt to
     * update any other mappings of this Map.
     *
     * @param key key with which the specified value is to be associated
     * @param remappingFunction the function to compute a value
     * @return the new value associated with the specified key, or null if none
     * @throws NullPointerException if the specified key or remappingFunction
     *         is null
     * @throws IllegalStateException if the computation detectably
     *         attempts a recursive update to this map that would
     *         otherwise never complete
     * @throws RuntimeException or Error if the remappingFunction does so,
     *         in which case the mapping is unchanged
     */
    // 如果key 存在,新的值不为null,那么更新key映射的值为新的值,如果新的值为null,那么删除此节点;
    // 如果key 不存在,新的值不为null,那么进行插入,如果新的值为null,那么什么都不做
    public V compute(K key,
                     BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        // 判空
        if (key == null || remappingFunction == null)
            throw new NullPointerException();
        // 计算key 的hash值
        int h = spread(key.hashCode());
        V val = null;
        int delta = 0;
        int binCount = 0;
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            if (tab == null || (n = tab.length) == 0)
                // 如果table还没初始化,那么初始化table
                tab = initTable();
            else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
                // 如果该索引位置上还没有元素,那么创建一个ReservationNode,把该索引位置占住
                Node<K,V> r = new ReservationNode<K,V>();
                // 先获取锁,再使用CAS设置值,避免CAS成功后,被其他线程先获取到了锁
                synchronized (r) {
                    if (casTabAt(tab, i, null, r)) {
                        binCount = 1;
                        Node<K,V> node = null;
                        try {
                            // 使用给定的函数计算
                            if ((val = remappingFunction.apply(key, null)) != null) {
                                // 如果计算的值不为null,那么插入到map中
                                delta = 1;
                                node = new Node<K,V>(h, key, val, null);
                            }

                            // 如果计算的值为null,那么什么都不做
                        } finally {
                            // 1、如果异常了或者新的值为null,那么会把该索引位置上的值设置回null
                            // 2、如果没有异常,那么会把该索引位置上的值设置为目标node
                            // ReservationNode 会被覆盖掉
                            setTabAt(tab, i, node);
                        }
                    }
                }
                // binCount != 0,说明数据插入完成了,也可能值为null,没有进行插入,
                // 如果是计算出现异常,那么线程不会执行到这里,上面就抛出异常了
                if (binCount != 0)
                    break;
            }

            // 如果当前正在进行扩容,那么先帮忙搬运数据
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);

            else {
                synchronized (f) {
                    if (tabAt(tab, i) == f) {
                        if (fh >= 0) {
                            binCount = 1;
                            for (Node<K,V> e = f, pred = null;; ++binCount) {
                                K ek;
                                if (e.hash == h &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    // 找到key 所在的节点,使用给定的函数计算值,参数传入 key,原来的值
                                    val = remappingFunction.apply(key, e.val);

                                    if (val != null)
                                        // 如果值不为null,替换原来的值
                                        e.val = val;
                                    else {
                                        // 如果计算出新的值为null,那么删除这个节点
                                        delta = -1;
                                        Node<K,V> en = e.next;
                                        if (pred != null)
                                            // pred.next 指向删除节点的下一个节点
                                            pred.next = en;
                                        else
                                            // 如果删除的节点是该索引位置上的第一个节点,那么修改
                                            // 第一个节点为: e.next
                                            setTabAt(tab, i, en);
                                    }
                                    break;
                                }
                                pred = e;

                                // 该索引位置上没有其他元素了,说明key在此map中不存在,进行插入处理
                                if ((e = e.next) == null) {
                                    // 使用给定的函数计算值,参数传入key,null
                                    val = remappingFunction.apply(key, null);
                                    if (val != null) {
                                        delta = 1;
                                        // 插入数据
                                        pred.next =
                                            new Node<K,V>(h, key, val, null);
                                    }
                                    // 如果计算的值为null,那么什么都不做

                                    break;
                                }
                            }
                        }
                        else if (f instanceof TreeBin) {
                            // binCount 设置为2,当存在竞争的时候可以进行扩容,当存在多线程竞争的时候 binCount <= 1,不会
                            // 进行扩容
                            binCount = 1;
                            TreeBin<K,V> t = (TreeBin<K,V>)f;
                            TreeNode<K,V> r, p;
                            if ((r = t.root) != null)
                                // findTreeNode()查找key对应的节点,没有返回null
                                p = r.findTreeNode(h, key, null);
                            else
                                // root = null,p 没有初始化,所以初始化为null (不会存在root = null的情况)
                                p = null;
                            V pv = (p == null) ? null : p.val;
                            // 使用给定的函数计算值,参数传入key, pv
                            val = remappingFunction.apply(key, pv);
                            if (val != null) {
                                // p != null,说明key已存在
                                if (p != null)
                                    // 使用新的值替换原来的值
                                    p.val = val;
                                else {
                                    // 插入数据
                                    delta = 1;
                                    t.putTreeVal(h, key, val);
                                }
                            }

                            // 新的值 val = null
                            else if (p != null) {
                                // p != null,而新的值为null,所以删除此节点
                                delta = -1;
                                // removeTreeNode()如果删除后红黑树的结构太小,如果返回true,表示需要把红黑树转成链表
                                if (t.removeTreeNode(p))
                                    setTabAt(tab, i, untreeify(t.first));
                            }
                        }
                    }
                }
                if (binCount != 0) {
                    // TREEIFY_THRESHOLD = 8。 binCount不包含新插入的元素,因此加上新插入的元素,slot中元素个数达到
                    // 9个才会转成红黑树
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    break;
                }
            }
        }

        // 更新元素个数
        if (delta != 0)
            addCount((long)delta, binCount);
        return val;
    }

    /**
     * If the specified key is not already associated with a
     * (non-null) value, associates it with the given value.
     * Otherwise, replaces the value with the results of the given
     * remapping function, or removes if {@code null}. The entire
     * method invocation is performed atomically.  Some attempted
     * update operations on this map by other threads may be blocked
     * while computation is in progress, so the computation should be
     * short and simple, and must not attempt to update any other
     * mappings of this Map.
     *
     * @param key key with which the specified value is to be associated
     * @param value the value to use if absent
     * @param remappingFunction the function to recompute a value if present
     * @return the new value associated with the specified key, or null if none
     * @throws NullPointerException if the specified key or the
     *         remappingFunction is null
     * @throws RuntimeException or Error if the remappingFunction does so,
     *         in which case the mapping is unchanged
     */
    public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
        // key 和 value 都不能为null,否则抛出NullPointerException
        if (key == null || value == null || remappingFunction == null)
            throw new NullPointerException();
        int h = spread(key.hashCode());
        V val = null;
        int delta = 0;
        int binCount = 0;
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();
            else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
                if (casTabAt(tab, i, null, new Node<K,V>(h, key, value, null))) {
                    delta = 1;
                    val = value;
                    break;
                }
            }
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);
            else {
                synchronized (f) {
                    if (tabAt(tab, i) == f) {
                        if (fh >= 0) {
                            binCount = 1;
                            for (Node<K,V> e = f, pred = null;; ++binCount) {
                                K ek;
                                if (e.hash == h &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    val = remappingFunction.apply(e.val, value);
                                    if (val != null)
                                        e.val = val;
                                    else {
                                        delta = -1;
                                        Node<K,V> en = e.next;
                                        if (pred != null)
                                            pred.next = en;
                                        else
                                            setTabAt(tab, i, en);
                                    }
                                    break;
                                }
                                pred = e;
                                if ((e = e.next) == null) {
                                    delta = 1;
                                    val = value;
                                    pred.next =
                                        new Node<K,V>(h, key, val, null);
                                    break;
                                }
                            }
                        }
                        else if (f instanceof TreeBin) {
                            binCount = 2;
                            TreeBin<K,V> t = (TreeBin<K,V>)f;
                            TreeNode<K,V> r 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值