美文网首页
HashMap并发情况下可引发的问题

HashMap并发情况下可引发的问题

作者: 牛逼的二进制 | 来源:发表于2019-06-23 11:43 被阅读0次

1)put数据,覆盖导致丢失

假设2个线程:1)同时put数据 2)hash后都落在table[I] 3)按如下顺序执行createEntry代码段,最后map的数据如图状态1,thread2的put丢失。

timeline thread1 thread2
1 Entry<K,V> e = table[bucketIndex];
2 Entry<K,V> e = table[bucketIndex];
3 table[bucketIndex] = new Entry<>(hash, key, value, e);
4 table[bucketIndex] = new Entry<>(hash, key, value, e);
put数据覆盖丢失
void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }

2)resize时,get返回null

假设2个线程:1)thread1 put数据触发resize 2)thead2 get数据,hash后都落在table[I] 3)且按如下顺序执行代码段,因为table[I]节点node.next==null,无法遍历链表,get返回null。

timeline thread1 thread2
1 while(null != e) {……}
只执行了一个循环,table变为table'
2 for (Entry<K,V> e = table[indexFor(hash, table.length)];e != null;e = e.next) {……}
table[I]节点e,e.next == null
get返回null
/**
     * Transfers all entries from current table to newTable.
     */
    void transfer(Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;
        for (Entry<K,V> e : table) {
            while(null != e) {
                Entry<K,V> next = e.next;
                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                int i = indexFor(e.hash, newCapacity);
                e.next = newTable[I];
                newTable[i] = e;
                e = next;
            }
        }
    }

/**
     * Returns the entry associated with the specified key in the
     * HashMap.  Returns null if the HashMap contains no mapping
     * for the key.
     */
    final Entry<K,V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }

        int hash = (key == null) ? 0 : hash(key);
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }

3)resize时,put导致数据丢失

假设2个线程:1)thread1 put触发resize 2)thread resize未完成时,thead2 put又触发resize 3)且按如下顺序执行代码段,最终table状态为newtable‘导致部分之前put的数据丢失。

timeline thread1 thread2
1 transfer(newTable, initHashSeedAsNeeded(newCapacity));
如图newtable
2 transfer(newTable, initHashSeedAsNeeded(newCapacity));
如图newtable’
3 table = newTable;
4 table = newTable;
同时resize
void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }

        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable, initHashSeedAsNeeded(newCapacity));
        table = newTable;
        threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
    }

    /**
     * Transfers all entries from current table to newTable.
     */
    void transfer(Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;
        for (Entry<K,V> e : table) {
            while(null != e) {
                Entry<K,V> next = e.next;
                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                int i = indexFor(e.hash, newCapacity);
                e.next = newTable[I];
                newTable[i] = e;
                e = next;
            }
        }
    }

4)resize死循环

可以看酷 壳 – CoolShell 的这篇文章:JAVA HASHMAP的死循环

注:
以上代码基于jdk1.7
IntelliJ IDEA 多线程debug

\color{red}{少看文章 多读代码!!!}
\color{red}{少看文章 多想代码!!!}
\color{red}{少看文章 多写代码!!!}

相关文章

网友评论

      本文标题:HashMap并发情况下可引发的问题

      本文链接:https://www.haomeiwen.com/subject/uhqiqctx.html