提问者:小点点

在调整大小时读取并发HashMap


我想知道当我们尝试在调整大小时读取ConCurrentHashMap时可能发生的情况。

我知道在读取过程中,第一次尝试总是不同步的。在第二次尝试中,它将尝试获取锁并重试。

但是如果它发生在调整大小期间,它将如何工作?

谢啦


共2个答案

匿名用户

通过查看来源:

并发HashMap包含一个或多个段,具体取决于并发级别。

在重新散列期间,一个段被锁定,新表在旧表旁边构建,然后在最后替换它。

如果在段的重新散列期间调用get()并且您的键存储在该段中,您将访问该段的旧表而不带锁,如果找到并且值不为空,则将返回。如果值为空,则调用将阻塞,直到重新散列完成,并且该值将在锁下再次读取。

匿名用户

Java8/11,

让我们先看看get()方法,

   /**
     * Returns the value to which the specified key is mapped,
     * or {@code null} if this map contains no mapping for the key.
     *
     * <p>More formally, if this map contains a mapping from a key
     * {@code k} to a value {@code v} such that {@code key.equals(k)},
     * then this method returns {@code v}; otherwise it returns
     * {@code null}.  (There can be at most one such mapping.)
     *
     * @throws NullPointerException if the specified key is null
     */
    public V get(Object key) {
        Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
        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) {
                if ((ek = e.key) == key || (ek != null && key.equals(ek)))  // found and just return
                    return e.val;
            }
            else if (eh < 0)   // -1 for forwarding node or red tree
                return (p = e.find(h, key)) != null ? p.val : null;
            while ((e = e.next) != null) { // iterate linked list
                if (e.hash == h &&
                    ((ek = e.key) == key || (ek != null && key.equals(ek))))
                    return e.val;
            }
        }
        return null;
    }

Java8用自旋锁(CAS和循环)代替段锁,并在碰撞桶的第一个节点上同步,以获得更好的并发性能。

有两个表用于调整大小,并通过易失性声明强制可见。

    /**
     * The array of bins. Lazily initialized upon first insertion.
     * Size is always a power of two. Accessed directly by iterators.
     */
    transient volatile Node<K,V>[] table;

    /**
     * The next table to use; non-null only while resizing.
     */
    private transient volatile Node<K,V>[] nextTable;

调整大小基本上是将数据从table复制到nextTable。完成后,nextTable将设置为table,旧的table将作为垃圾免费。

现在通过get方法查找目标桶时,如果它还没有被处理,我们只是通过比较键来查找值。如果它已经被移动到新表中,则会在这个桶中设置一个转发节点,指向新表,因此e. find(h,key)会通过键在nextTable中搜索bucket。