HashMap为什么是线程不安全的?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HashMap为什么是线程不安全的?相关的知识,希望对你有一定的参考价值。

问题:HashMap为什么线程不安全?

一、hash碰撞导致
HashMap的底层存储结构,HashMap底层是一个Entry数组(键值对),一旦发生Hash冲突的的时候,HashMap采用拉链法解决碰撞冲突,Entry内部的变量:

final Object key;
Object value;
Entry next;
int hash;

通过Entry内部的next变量可以知道使用的是链表,这时候我们可以知道,如果多个线程,在某一时刻同时操作HashMap并执行put操作,而有大于两个key的hash值相同,这个时候需要解决碰撞冲突,而解决冲突的办法另一篇已经说过(线性探测法+拉链桶),对于链表的结构在这里不再赘述,暂且不讨论是从链表头部插入还是从尾部初入,这个时候两个线程如果恰好都取到了对应位置的头结点e1,而最终的结果可想而知,a1、a2两个数据中势必会有一个会丢失,再来看下put方法不是同步的


public Object put(Object obj, Object obj1)  
    {  
        if(table == EMPTY_TABLE)  
            inflateTable(threshold);  
        if(obj == null)  
            return putForNullKey(obj1);  
        int i = hash(obj); //位置 
        int j = indexFor(i, table.length);  
        for(Entry entry = table[j]; entry != null; entry = entry.next)  
        {  
            Object obj2;  
            if(entry.hash == i && ((obj2 = entry.key) == obj || obj.equals(obj2)))  
            {  
                Object obj3 = entry.value;  
                entry.value = obj1;  
                entry.recordAccess(this);  
                return obj3;//返回的是旧value(元素)  
            }  
        }  

        modCount++;  
        addEntry(i, obj, obj1, j);  
        return null;  
    }  


二、扩容导致

HashMap存在扩容也是HashMap非线程安全的原因:

void resize(int i)  
    {  
        Entry aentry[] = table;  
        int j = aentry.length;  
        if(j == 1073741824)  
        {  
            threshold = 2147483647;  
            return;  
        } else  
        {  
            Entry aentry1[] = new Entry[i];  
            transfer(aentry1, initHashSeedAsNeeded(i));  
            table = aentry1;  
            threshold = (int)Math.min((float)i * loadFactor, 1.073742E+009F);  
            return;  
        }  
    }

以上是关于HashMap为什么是线程不安全的?的主要内容,如果未能解决你的问题,请参考以下文章

hashmap多线程不安全的原因

面试官:小伙子,你给我说一下HashMap 为什么线程不安全?

面试官:小伙子,你给我说一下HashMap 为什么线程不安全?

面试官:小伙子,你给我说一下HashMap 为什么线程不安全?

面试官:小伙子,你给我说一下HashMap 为什么线程不安全?

HashMap为啥不安全?