0
点赞
收藏
分享

微信扫一扫

为什么多线程并发下不能使用HashMap?

alonwang 2022-01-10 阅读 49

问题:
我们在工作中经常使用HashMap这个数据结构,每一个程序猿都被告诫,这个数据结构不是线程安全的,在单线程操作HashMap的情况下,程序不会问题,但是如果多线程操作同一个HashMap,会出现cpu使用率百分百的情况,这是为什么咧?

1、百度一下的话,网上文章会告诉你答案,cpu使用率达到100%是因为HashMap在多线程并发的情况下entryList出现了环导致的

我们接下来探究,为什么HashMap在多线程并发的情况下entryList会出现了环:其实这一切来源于HashMap的扩容

举个例,这个例子来源一位大佬的分享:
如果两个线程同时对hashMap进行扩容,其中有个线程执行的速度稍微快一点,HashMap扩容代码如下:

 for (int j = 0; j < src.length; j++) {
5:    Entry e = src[j];
6:    if (e != null) {
7:      src[j] = null;
8:      do {
9:      Entry next = e.next;
     // Thread1 STOPS RIGHT HERE
10:     int i = indexFor(e.hash, newCapacity);
11:     e.next = newTable[i];
12:     newTable[i] = e;
13:     e = next;
14:   } while (e != null);
15:   }
16: }

当线程一在第10行的位置发生了上下文切换, 此时线程一已经设置了节点A(e1)和A的下一个节点是B(next1),如下图所示:
在这里插入图片描述
此时线程一没有移动任何节点,仅仅是分配了一个新的存储桶数组空间。

此时线程2对HashMap进行了扩容操作,扩容后的entryList,如下图所示 :
在这里插入图片描述
但是此时在线程1中,请注意,e1 和 next1 仍指向相同的节点。但这些节点被洗牌了。最重要的是,下一个关系被逆转了。也就是说,当 线程1 启动时,它具有节点 A,其下一个节点为节点 B。现在,情况正好相反,节点 B 的下一个节点是节点 A。但是线程1对于线程二的操作并不知情。以下是当线程1获得cpu资源继续往下执行时采取的操作:
在这里插入图片描述


下一次迭代将把 A 放到存储桶 3 列表的前面(毕竟是下一个)。并将它的next节点分配给B,而此时B节点的next节点实际已经分配给了A,产生了环,如下图所示:
在这里插入图片描述
参考资料:
1、http://mailinator.blogspot.com/2009/06/beautiful-race-condition.html
2、https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6423457

举报

相关推荐

0 条评论