热门IT资讯网

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;          }      }
0