源码阅读请参考HashMap-jdk1.7
假设有两个线程T1,T2
1.漏值
void createEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];//1
table[bucketIndex] = new Entry<>(hash, key, value, e);
size++;
}
假设T1和T2的key哈希散列值一样,当T1执行到1处,cpu时间片切换,T2开始执行并且执行完整个createEntry
,单当cpu时间片切回T1,那么这时候T1的值就会覆盖掉T2的值,造成T2的值消失了。
2.死循环
这个发生在数组扩容的时候。先看关键代码。
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
for (Entry<K,V> e : table) {
while(null != e) {
Entry<K,V> next = e.next;//1
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
}
}
}
假设
table[3]=a; a.next=b; b.next=null;
T1,T2进行put的时候都发生了扩容,T1先执行,到1的地方cpu切换,此时对于T1来讲
e=a;next=b;
接下来T2执行并且执行完整个流程,那么此时对于这个hashmap来讲
table[3]=b; b.next=a; a.next=null;
cpu切回T1,执行第一遍循环
a.next=newtable[3]=null; newtable[3]=a; e=next=b;
结果就是
newtable[3]=a; a.next=null; table[3]=b; b.next=a;
执行第二遍循环
e=b; next=b.next=a;
b.next=newtable[3]=a; newtable[3]=b;e=a;
即
newtable[3]=b; b.next=a; a.next=null;
执行第三遍循环
e=a;next=null;
a.next=newtavle[3]=b; newtable[3]=a;e=null;
即
newtable[3]=b; b.next=a ; a.next=b;
等T1执行完table=newtable,下次遍历链表操作的时候就会发生死循环。