HashMap为什么是线程不安全的?
发表于:2024-11-30 作者:热门IT资讯网编辑
编辑最后更新 2024年11月30日,问题:HashMap为什么线程不安全?一、hash碰撞导致HashMap的底层存储结构,HashMap底层是一个Entry数组(键值对),一旦发生Hash冲突的的时候,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; } }