重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
问题: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;
}
}