HashMap多线程操作下的问题总结
2017-09-04 00:13
267 查看
HashMap多线程操作下的问题总结
前段时间海外库存系统隔一段时间就会出现CPU使用率告警。最终排查出来,是由于海外库存在接收多线程数据查询结果时,使用了一个普通的HashMap来接收,也就是多个线程对同一个HashMap进行非线程安全的put操作导致的。经证实,海外库存的数据查询偶尔出现非预期结果,也与此有关:比如有库存的商品,查出来却是0等等。HashMap多线程操作会造成一系列问题,这很多人都知道。但反过来根据现象查问题,可能就不那么明显了。因此这里对多线程下HashMap使用会造成的问题做个小总结,以供大家“根据现象反查问题”作参考。
问题1. 导致死循环,CPU使用率飙升
特征1:生产环境某个实例CPU使用率飙升,并且多次thread dump显示同一个线程在很长一段时间内一直在对同一个HashMap在做put、get、或者遍历操作。
特征2:每当一个线程进入死循环,就会占用100%/CPU核数 的CPU利用率,我们的生产环境是4核CPU,因此可以看 到,生产环境上有25%左右、50%左右、75%左右以及99%的CPU利用率(99%利用率的时候会告警,我们就去重启了,所以这里看不到)。
下图左边是 修复HashMap多线程使用前,日常的CPU利用率,右图是修复后一周的日常CPU利用率。大致原理
HashMap的内部存储结构如下,由一个数组,以及数组上的链表组成:其中,key的Hash值与数组长度取模得出的值相等的元素将会放在同一个链表上。而HashMap查找的过程,实际就是根据key计算hash值,与当前长度取模,从而定位到数组中的某个链表,然后再从这个链表上进行遍历查找数据。
在put的过程中,随着hashmap元素个数的增长,链表越来越长,Map查找的效率会越来越低。因此当数量增长到一定时候,一般是为 元素个数 > 数组length * loadFactor。loadFactor默认0.75,可以自己定义。就会对数组进行扩容,并且遍历原来的HashMap中的所有元素,将原有元素全部重新put到新的数组及链表中。
在多线程情况下,put的过程在操作同一个链表时,会形成如下循环链表(这里要讲篇幅就长了,网上关于HahsMap扩容过程的资料很多)。当进行get查询,或者遍历操作的时候,就会进行链表的死循环遍历,从而导致CPU占用彪高。
问题2. Map.size()与实际不合
多线程环境下put的HashMap会被“损坏”,其中会造成size与实际不符合,以下代码中,如果有幸没有进入死循环,assert断言有很大概率不会通过。Map<String, Object> testMap = new HashMap<>(); // TODO 多线程环境下对testMap进行put操作 ... // 。。。 。。。 // 多线程对testMap进行put操作完成 int realSize = 0; for (Entry entry : testMap.entrySet()){ realSize += 1; } // assert 很大概率失败 assert realSize == testMap.size();
Map.size与实际不合将会导致一些依赖Map.size的业务逻辑出现不可预知的异常。
问题3. 数据丢失
多线程环境下put的HashMap会造成数据丢失,明明put进去的数据,却get不到了。下边的一段代码中,如果运气好,没有进入死循环,那么assert断言也有很大可能性过不了。
Map<Integer, Object> testMap = new HashMap<>(); Object val = new Object(); CountDownLatch cdl = new CountDownLatch(100); for (int i = 0; i < 100 ; i++) { new Thread(new Runnable() { @Override public void run() { for (int j = i * 100; j < (i + 1) * 100 ; j++ ) { testMap.put(j, val); } cdl.countDown(); } }).start(); } cdl.await(); for (int i = 0; i < 10000; i++ ) { // assert 很大概率会失败 assert testMap.get(i) != null; }
相关文章推荐
- Java Hashmap多线程同时操作导致的问题
- 多线程环境下操作HashMap的问题
- JAVA问题总结之24--HashMap键值对(key-value)的操作
- Java【多线程知识总结(7)】多线程同步问题-关于synchronized代码块和synchronized方法的应用
- 40个Java多线程问题总结
- Qt 多线程与数据库操作需要注意的几点问题
- 40个Java多线程问题总结
- 链表操作类型的问题总结
- 40个Java多线程问题总结
- HashMap多线程死循环问题
- 多线程之Map:Hashtable HashMap 以及ConcurrentHashMap的线程安全问题
- 定位多线程内存越界问题实践总结
- 40个Java多线程问题总结
- 定位多线程内存越界问题实践总结
- 40个多线程问题总结
- 多线程生产者与消费者问题的总结
- asp.net操作IIS主机头的问题总结
- C#对数据库操作注意问题总结
- 40个Java多线程问题总结
- HashMap源码和常见问题分析以及多线程开发时的问题