聊一聊JDK 7的HashMap中的“死锁”是怎么回事?
HashMap
是线程不安全的,在HashMap
的源码中并未对其操作进行同步执行,所以在并发访问的时候就会出现线程安全的问题。
由于上一篇的ConcurrentHashMap
篇中讲到了死锁,也画了图,但是很多读者说看不懂,这里我的锅,在这里详细的进行图解。
假设:有线程A和线程B,并发访问HashMap中的数据。假设HashMap
的长度为2
(这里只是为了讲解方便假设长度为2),链表的结构图如下所示:
4和8都位于同一条链表上,其中的threshold为1,现在线程A和线程B都要进行put操作,首先线程A进行插入值。
此时,线程A执行到transfer
函数中(transfer
函数是resize扩容方法中调用的另一个方法),当执行(1)
位置的时候,如下所示:
/**
* Transfers all entries from current table to newTable.
*/
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;
} // while
}
}
此时线程A挂起,在此时在线程A的栈中就会存在如下值:
e = 4
next = 8
此时线程B执行put
的操作,并发现在进行put
操作的时候需要扩容,当线程B执行 transfer
函数中的while
循环,即会把原来的table
变成新一table
(线程B自己的栈中),再写入到内存中。
执行的过程如下图所示(假设两个元素在新的hash函数下也会映射到同一个位置):
此时线程A有获取到cpu
的执行时间,接着执行(但是纤层A中的数据仍是旧表数据),即从transfer
代码(1)处接着执行,当前的 e = 4, next = 8
, 上面已经描述,执行的的过程若下图所示:
当操作完成,执行查找时,会陷入死循环!