1. List遍历时修改报错

别的先什么都不说,直接上代码看看就知道了:

 public class ListTest {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(1);list.add(2);list.add(3);list.add(4);
Iterator iterator = list.iterator();
while(iterator.hasNext()) {
int currentInt = Integer.parseInt(iterator.next().toString());
if (currentInt == 2) {
list.remove(currentInt);
} else {
System.out.println(currentInt);
}
}
}

运行以上代码报错如下所示:

  

  查看jdk源代码中AbstractList的checkForComodification方法的源码,分析产生ConcurrentModificationException异常的原因发现错误原因了,具体解释请看下面一张图片:

  

  要想解决以上问题,需要使用并发集合,对应ArrayList的并发集合为:CopyOnWriteArrayList 。具体详情介绍请看下一篇:并发应用之十三 & 并发集合应用

 2. HashMap多线程下死循环问题

  Race Condition(也叫做资源竞争),是多线程编程中比较头疼的问题。特别是Java多线程模型当中,经常会因为多个线程同时访问相同的共享数据,而造成数据的不一致性。为了解决这个问题,通常来说需要加上同步标志“synchronized”,来保证数据的串行访问。但是“synchronized”是个性能杀手,过多的使用会导致性能下降,特别是扩展性下降,使得你的系统不能使用多个CPU资源。 这是我们在性能测试中经常遇见的问题。

  但是:有时候使用HashMap并没有使用 synchronized 同步反而性能下降很厉害,几乎将CPU资源打满了。

   于是做了好几轮测试与验证,具体做法如下:

    1. 让并发用户数量为1,不停的运行10分钟,结果没有发现这种情况;

    2. 接着我们让50个并发用户同时运行,但是只运行在一个CPU上(通过psrset),结果也没有出现死循环状态。

    3. 只要并发用户数量超过10个,运行的CPU超过两个,不到2分钟就出现死循环。一旦死循环出现,大量CPU资源被白白浪费,性能会非常非常的差。

  通过上面的试验我们可以很肯定的判断,是由于并发控制不好,导致数据的不一致,引起的死循环。值得一提的是,HashMap不是一个线程安全的数据结构,要用到多个线程中去,需要自己加上同步标志,为什么会死循环呢,看看下面HashMap中get函数的源代码:

 public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null;e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}

  get函数会根据key的hashcode来锁定多个对象,并且遍历这些对象来找到key所对应的对象。当多个线程不安全的修改HanshMap数据结构的时候,有可能使得这个函数进入死循环。

建议可以使用并发集合:ConcurrentHashMap 或在使用HanshMap的时候加上同步标志!

  具体遍历这些对象为什么会发生死循环呢,其实HashMap底层循环还是采用迭代器进行的,当循环取数据的时候,如果有其他线程在修改确实可能会发生死循环,具体过程我这里用伪代码进行讲解说明:(假设数据集合总个数为4个)

count = 4;
while (hasNext()) {
next();//取出对应的数据
cursor++;//对应版本号
} hasNext() {
if (cursor == count) {
return false;//取到最后一个元素了
}
return true;
} remove() {
count--;
   cursor++;
} /**
* 如果正好遍历到最后一个元素,正好有其他线程执行remove操作,则count=3
* 这样 cursor 永远不等于 count 了,从上面 hasNext()判断方法看,判断是否有下一个值就永远是真,即为死循环
*/

 总结:在平时使用集合编写代码的时候,以上两个错误是我们经常遇见的。如果真发生了这样的情况,那么我建议直接换用线程并发集合,这样可以解决以上的问题。

提示:欢迎继续参看我相关的下一篇博客:并发库应用之十三 & 并发集合类的应用

并发库应用之十二 & 常用集合问题汇总的更多相关文章

  1. 20190825 On Java8 第十二章 集合

    第十二章 集合 java.util 库提供了一套相当完整的集合类(collection classes)来解决这个问题,其中基本的类型有 List . Set . Queue 和 Map. 不要在新代 ...

  2. 并发编程从零开始(十二)-Lock与Condition

    并发编程从零开始(十二)-Lock与Condition 8 Lock与Condition 8.1 互斥锁 8.1.1 锁的可重入性 "可重入锁"是指当一个线程调用 object.l ...

  3. 【西北师大-2108Java】第十二次作业成绩汇总

    [西北师大-2108Java]第十二次作业成绩汇总 作业题目 面向对象程序设计(JAVA) 第14周学习指导及要求 实验目的与要求 (1)掌握GUI布局管理器用法: (2)掌握Java Swing文本 ...

  4. 并发库应用之十 & 多线程数据交换Exchanger应用

    申明:用大白话来说就是用于实现两个人之间的数据交换,每个人在完成一定的事务后想与对方交换数据,第一个先拿出数据的人会一直等待第二个人,直到第二个人拿着数据到来时,才能彼此交换数据. java.util ...

  5. Java笔记(二十二)……Collection集合

    概述 为什么会出现集合类 面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式 数组和集合类同是容器,有何不同 数组虽然也可以存储 ...

  6. 四十二 常用内建模块 collections

    collections是Python内建的一个集合模块,提供了许多有用的集合类. namedtuple 我们知道tuple可以表示不变集合,例如,一个点的二维坐标就可以表示成: >>> ...

  7. Java基础(二十二)集合(4)Set集合

    Set集合为集类型.集是最简单的一种集合,存放于集中的对象不按特定方式排序,只是简单地把对象加入集合中.对集中存放的对象的访问和操作时通过对象的引用进行的,所以,在集中不能存放重复对象. Set接口实 ...

  8. Oracle笔记(十二) 集合、序列

    一.集合 在数学的操作之中存在交.差.并.补的概念,而在数据的查询中也存在此概念,有如下几个连接符号: UNION:连接两个查询,相同的部分不显示: UNION ALL:连接两个查询,相同的部分显示: ...

  9. Java并发编程的艺术(十二)——并发容器和框架

    ConcurrentHashMap 为什么需要ConcurrentHashMap HashMap线程不安全,因为HashMap的Entry是以链表的形式存储的,如果多线程操作可能会形成环,那样就会死循 ...

随机推荐

  1. 不要使用jQuery触发原生事件

    原文链接: DO NOT TRIGGER REAL EVENT NAMES WITH JQUERY! 原文日期: 2014年02月26日 翻译日期: 2014年03月2日 翻译人员: 铁锚 JavaS ...

  2. R.java文件介绍

    HelloWorld工程中的R.java文件 package com.android.hellworld; public final class R {     public static final ...

  3. 【一天一道LeetCode】 #3 Longest Substring Without Repeating Characters

    一天一道LeetCode (一)题目 Given a string, find the length of the longest substring without repeating charac ...

  4. Gradle 1.12用户指南翻译——第二十三章. Java 插件

    其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://g ...

  5. Linux常用命令(第二版) --帮助命令

    帮助命令 1.man[manual]: /usr/bin/man 获得命令或配置文件的帮助文档,优先查看命令的帮助 格式:man [命令或配置文件]  #会同时调用more E.g. man ls # ...

  6. cocos2d 从v1.x升级到v2.x需要注意的几个地方

    首先v1.x一些CCNode定位函数实现的有问题,导致返回的CCPoint的x坐标不正确(超出320后无变化),怀疑是其对屏幕旋转判断的不正确;而且这种现象在iOS 7.1之前的模拟器中运行都正常,在 ...

  7. Linux input子系统分析

    输入输出是用户和产品交互的手段,因此输入驱动开发在Linux驱动开发中很常见.同时,input子系统的分层架构思想在Linux驱动设计中极具代表性和先进性,因此对Linux input子系统进行深入分 ...

  8. SharePoint 解决方案手动打包简单介绍

    介绍:在使用SharePoint中,我们经常需要做的就是打包解决方案,我们来介绍下SharePoint解决方案的手动部署,我自己觉得,解决方案是SharePoint中非常好的一个功能,部署和使用起来相 ...

  9. Github搜索技巧-如何使用github找到自己感兴趣的项目

    Github现在不仅仅作为一个版本控制工具,更是一个开源的仓库,里面不但有优秀的开源代码,电子书,还有一些五花八门的项目,有些国家的法律也放在上面,作为程序员如何找到自己感兴趣的项目就非常重要了! 欢 ...

  10. 如何在服务器上配置ODBC来访问本机DB2 for Windows服务器

    如何在服务器上配置ODBC来访问本机 DB2 for Windows服务器                         马根峰             (广东联合电子服务股份有限公司, 广州 51 ...