Java集合类中,某个线程在 Collection 上进行迭代时,通常不允许另一个线性修改该 Collection。通常在这些情况下,迭代的结果是不确定的。如果检测到这种行为,一些迭代器实现(包括 JRE 提供的所有通用 collection 实现)可能选择抛出此异常。执行该操作的迭代器称为快速失败 迭代器,因为迭代器很快就完全失败,而不会冒着在将来某个时间任意发生不确定行为的风险。

因此,当一个线程试图ArrayList的数据的时候,另一个线程对ArrayList在进行迭代的,会出错,抛出ConcurrentModificationException。

比如下面的代码:

finalList tickets =newArrayList();

for(inti =0; i <100000; i++) {

tickets.add("ticket NO,"+ i);

}

System.out.println("start1...");

for(inti =0; i <10; i++) {

Thread salethread = newThread() {

public voidrun() {

while(tickets.size() >0) {

tickets.remove(0);

System.out.println(Thread.currentThread().getId()+"Remove 0");

}

}

};

salethread.start();

}

System.out.println("start2...");

newThread() {

public voidrun() {

for(String s : tickets) {

System.out.println(s);

}

}

}.start();

上述程序运行后,会在某处抛出异常:

java.util.ConcurrentModificationException  at java.util.ArrayList$Itr.checkForComodification(Unknown Source)  at java.util.ArrayList$Itr.next(Unknown Source)  at mytest.mytestpkg.Tj$2.run(Tj.java:138)

Vector是线程同步的,那么把ArrayList改成Vector是不是就对了呢?

答案是否定的,事实上,无论是ArrayList还是Vector,只要是实现Collection接口的,都要遵循fail-fast的检测机制,即在迭代是时候,不能修改集合的元素。一旦发现违法这个规定就会抛出异常。

--------------------------------------------------------------------------------

事实上,Vector相对于ArrayList的线程同步,体现在对集合元素是否脏读上。即ArrayList允许脏读,而Vector特殊的机制,不会出现脏读,但是效率会很差。

举个例子,一个集合,有10个线程从该集合中删除元素,那么每个元素只可能由一个线程删除掉,不可能会出现一个元素被多个线程删除的情况。

比如下面的代码:

finalList tickets =newArrayList();

for(inti =0; i <100000; i++) {

tickets.add("ticket NO,"+ i);

}

System.out.println("start1...");

for(inti =0; i <10; i++) {

Thread salethread = newThread() {

public voidrun() {

while(true) {

if(tickets.size()>0)

System.out.println(Thread.currentThread().getId()+ tickets.remove(0));

else

break;

}

}

};

salethread.start();

}

for循环构造10个线程删除同一个集合中的数据,理论上只能删除100000次。但是运行完发现,输出的删除次数108494次,其中很多数据都是被多个线程删除,比如下面的输出片段:

17ticket NO,35721  14ticket NO,35699  11ticket NO,35721  18ticket NO,35721  17ticket NO,35729  11ticket NO,35729  14ticket NO,35729  17ticket NO,35729  14ticket NO,35734  17ticket NO,35734  13ticket NO,35721

可以看到35721,35729都被多个线程删除。这事实上就是出现了脏读。解决的办法就是加锁,使得同一时刻只有1个线程对ArrayList做操作。

修改代码,synchronized关键字,让得到锁对象的线程才能运行,这样确保同一时刻只有一个线程操作集合。

finalList tickets =newArrayList();

for(inti =0; i <100000; i++) {

tickets.add("ticket NO,"+ i);

}

System.out.println("start1...");

finalObject lock=newObject();

for(inti =0; i <10; i++) {

Thread salethread = newThread() {

public voidrun() {

while(true) {

synchronized(lock)

{

if(tickets.size()>0)

System.out.println(Thread.currentThread().getId()+ tickets.remove(0));

else

break;

}

}

}

};

salethread.start();

}

这样得到的结果就是准确的了。

当然,不使用synchronized关键字,而直接使用vector或者Collections.synchronizedList 也是同样效果:

finalList tickets =java.util.Collections.synchronizedList(newArrayList());

finalList tickets =newVector();

vector和Collections.synchronizedList 都是线程同步的,避免的脏读的出现。

posted on 2013-02-28 11:33 顺其自然EVO 阅读(175) 评论(0)  编辑  收藏

java 多线程集合操作_多线程中使用Java集合类相关推荐

  1. java基础----集合操作---实例----List集合的初始化

    文章内容:最近使用LeapMotion进行软件开发时,使用到了java API---->List集合,使用过程中遇到了一些小bug,特写此博客记录相关心得. 程序编写背景:使用leapMotio ...

  2. java反射回调函数_用J2V8注册Java回调函数

    J2V8是一套针对谷歌的V8 JavaScript引擎的Java绑定.J2V8的开发为Android平台带来了高效的Javascript的执行环境,taris.js 就是基于J2V8开发的.J2V8同 ...

  3. java 多线程不安全_多线程并发为什么不安全

    一.线程安全定义 ​定义: ​多个线程访问同一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他操作,调用这个对象的行为都可以获得正确的结果 ...

  4. java 线程间通信方式_「转」JAVA多线程之线程间的通信方式

    1. 同步 这里讲的同步是指多个线程通过synchronized关键字这种方式来实现线程间的通信. public class MyObject { synchronized public void m ...

  5. java list sublist方法_(转)Java 中 List.subList() 方法的使用陷阱

    原文:http://blog.csdn.net/cleverGump/article/details/51105235 前言 本文原先发表在我的 iteye博客: http://clevergump. ...

  6. java使用集合存储过程_详解java调用存储过程并封装成map

    详解java调用存储过程并封装成map 发布于 2020-5-1| 复制链接 摘记: 详解java调用存储过程并封装成map           本文代码中注释写的比较清楚不在单独说明,希望能帮助到大 ...

  7. java byte 判断相等_你真的了解Java中quot;==quot;和equals()的区别?

    部分面试资料链接:https://pan.baidu.com/s/1qDb2YoCopCHoQXH15jiLhA 密码:jsam 想获得全部面试必看资料,关注公众号,大家可以在公众号后台回复" ...

  8. java读取csv文件_使用扫描仪读取Java中的CSV文件

    java读取csv文件 We can use Java Scanner Class to read CSV File in java. 我们可以使用Java扫描程序类在Java中读取CSV文件. 读取 ...

  9. java cpu分配_容器中的Java:资源分配准则

    短短几年内,容器就改变了软件行业的面貌.也许您已经到了在容器中运行Java的地步.那很棒!不幸的是,关于容器化Java应用程序的CPU和内存使用率,还有一些事情要注意,我将在下面概述. 本文假定总体上 ...

最新文章

  1. matlab 函数 向量参数,Scipy integrate(quad,quadration,nquad)不能集成向量参数化函数?等效函数(MATLAB works)...
  2. mysql 类似 user__类似于微博 用户最后一条动态的查询
  3. Mybatis分库分表扩展插件
  4. 将客户端移植到Linux和MAC OS
  5. 编写start脚本程序_使用可编写脚本的终结点遍历REST应用程序
  6. mysql select 效能_MYSQL的联合查询最好是少用,效能差异巨大
  7. javascript中数组、冒泡排序、函数及函数实参形参、arguments伪数组、异步函数等介绍
  8. 更多核心、更大内存、更低成本 AMD皓龙6000欲成云计算基石
  9. linux使用find命令_如何在Linux中使用FIND
  10. 在c语言中逗号运算符若不带括号,详解shell脚本括号区别--$()、$「 」、$「 」 、$(()) 、「 」 、「[ 」]...
  11. 混凝土静力受压弹性模量试验计算公式_混凝土试块检测要知道的9个技巧
  12. Centos7配置Samba服务实现与Windows文件共享
  13. Unity Android记录
  14. xp怎么删除计算机用户,WinXp系统如何删除用户账户?Xp系统删除用户账号的方法...
  15. 阿里系盒子英菲克i6八核 科学使用 笔记 (2015年12月26日成功)
  16. “AI复活了我的妻子,但我决定跟她说再见了”
  17. windows10系统超全面优化攻略
  18. 3DTouch Demo
  19. DIY 3D打印机——【有啥用啥版】
  20. 【Java】接口与继承

热门文章

  1. Java多线程(1)—线程初探
  2. MySQL中的数据分组
  3. Oracle数据库相关
  4. Dubbo服务调用失败
  5. Redis的Set操作
  6. Nginx的正向代理与反向代理
  7. SQLserver模糊查询
  8. java用流体加减乘除_任意输入两个数,完成加法、减法、乘法、除法运算!(加减乘除运算分别定义四个方法)_学小易找答案...
  9. 查看idea的错误日志信息
  10. 算法--06年华为面试:求两个数组的最小差值(Java实现)