主要是同一个hashmap在多线程情况下执行扩容方法后,再去调用Get方法可能会发生死循环的。
首先:
链表的next只是引用。保存的是引用指向的地址。所以多线程情况下可能会出现其他线程改变了next指向的目标后,当前线程继续执行。
读取时发生死循环的原因:
正常的 a->b->c 变成了a->b 而且 b->a。
发生的具体情况的例子如下:
前提:两个线程同时调用一个hashMap的扩容方法。
0.原先的hashmap链是 a->b->c
1.线程1 执行(Entry<K,V> next = e.next;)后。读取了a 和a.next=b之后 线程时间片用完,调用线程2
2.线程2 完整的执行整个rehash操作,使hashmap得链变成了c->b->a(jdk1.7中hashmap的rehash用的是头插法)
3.继续执行线程1 的循环操作
第一次循环:线程1中的newTable变成了a->null
第二次循环:线程1中的newTable变成了b->a
第三次循环:线程1中的newTable变成了a->b->a
具体代码的执行情况如下:
线程1 第一次循环
while(null != e) {
Entry<K,V> next = e.next;// next指向b
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i]; // e.next = null
newTable[i] = e;// newTable[i] =a 所以 newTable的链是 a->null
e = next;// 把e指向b
}
线程1 第二次循环
while(null != e) {
Entry<K,V> next = e.next;// e是b next指向a(由于线程2已经执行完毕,已经把b的next指向了a)
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];// e.next = a
newTable[i] = e;// 这里相当于把newtable的链从a->null变成b->a
e = next; //把e指向 a
}
线程1 第三次循环
while(null != e) {
Entry<K,V> next = e.next;// e是a next指向null
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];// e.next = (b->a) 所以e指向的是:a->(b->a)//这里形成了死循环
newTable[i] = e;// 这里相当于把newtable的链从b->a 变成了a->(b->a)
e = next; //把e指向 null 退出循环
}
网友评论