如题,最近项目里有个模块我做了异步处理方面的事情,在code过程中发现一个颠覆我对synchronized这个关键字和用法的地方,请问各位java开发者们是否对此有一个合理的解释,不多说,我直接贴出问题代码:

(事实证明这是一个坑,各位读者,如果有兴趣,可以先不看答案,自己看看能不能发现这个坑)

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;public class ConcurrentList {//private static List<String> TEST_LIST = new CopyOnWriteArrayList<String>();private static List<String> TEST_LIST = Collections.synchronizedList(new ArrayList<String>());public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {while (true) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}synchronized (TEST_LIST) {TEST_LIST.add("11");}System.out.println("Thread1 running");}}}).start();new Thread(new Runnable() {@Overridepublic void run() {while (true) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}synchronized (TEST_LIST) {for (String at : TEST_LIST) {TEST_LIST.add("22");}}System.out.println("Thread2 running");}}}).start();}
}

输出结果是:

Thread1 running
Exception in thread "Thread-1" java.util.ConcurrentModificationExceptionat java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)at java.util.AbstractList$Itr.next(AbstractList.java:343)at com.free4lab.lol.ConcurrentList$2.run(ConcurrentList.java:40)at java.lang.Thread.run(Thread.java:619)
Thread1 running
Thread1 running
Thread1 running
Thread1 running
Thread1 running
Thread1 running
Thread1 running
Thread1 running

-----------------------------------分隔线,以下是解释--------------------------------

问题明了了:

以上问题不是并发的问题,是ArrayList的问题,是个坑!且看如下代码,以及运行结果:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;public class ConcurrentList {//private static List<String> TEST_LIST = new CopyOnWriteArrayList<String>();private static List<String> TEST_LIST = Collections.synchronizedList(new ArrayList<String>());public static void main(String[] args) {TEST_LIST.add("111");TEST_LIST.add("222");for (String at : TEST_LIST) {System.out.println(at);TEST_LIST.add("333");System.out.println("add over");}}
}

结果是:

111
add over
Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)at java.util.AbstractList$Itr.next(AbstractList.java:343)at com.free4lab.lol.ConcurrentList.main(ConcurrentList.java:15)

分析:我们发现迭代了一次之后就抛出所谓的并发修改异常,不过这里没有多线程,看下源代码就知道了

list.add的时候执行了,修改了modCount,循环外面一次add到第一次迭代不会有问题,因为初始化的时候在AbstractList中int expectedModCount = modCount;,

/*** Appends the specified element to the end of this list.** @param e element to be appended to this list* @return <tt>true</tt> (as specified by {@link Collection#add})*/public boolean add(E e) {ensureCapacity(size + 1);  // Increments modCount!!elementData[size++] = e;return true;}public void ensureCapacity(int minCapacity) {modCount++;int oldCapacity = elementData.length;if (minCapacity > oldCapacity) {Object oldData[] = elementData;int newCapacity = (oldCapacity * 3)/2 + 1;if (newCapacity < minCapacity)newCapacity = minCapacity;// minCapacity is usually close to size, so this is a win:elementData = Arrays.copyOf(elementData, newCapacity);}}

public E next() {checkForComodification();try {E next = get(cursor);lastRet = cursor++;return next;} catch (IndexOutOfBoundsException e) {checkForComodification();throw new NoSuchElementException();}}

这样迭代器next()第一次 checkForComodification() 是不会抛出异常的,第二次才会抛出异常,因为在checkForComodification()里检查了

final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}
}

这样,在循环迭代中,进行了一次add操作,修改了modcount变量,再次迭代的时候,异常就throw出来了!

如果非要进行这样的操作,那么声明list为CopyOnWriteArrayList,就ok!因为用了copyonwrite技术

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;public class ConcurrentList {private static List<String> TEST_LIST = new CopyOnWriteArrayList<String>();//private static List<String> TEST_LIST = Collections.synchronizedList(new ArrayList<String>());public static void main(String[] args) {TEST_LIST.add("111");TEST_LIST.add("222");for (String at : TEST_LIST) {System.out.println(at);TEST_LIST.add("333");System.out.println("add over");}}
}

输出是正确的:

111
add over
222
add over

额外再说一点,也可以用iterator迭代,不过同样也无法调用next()方法(我注释掉了),这样程序就是死循环了,不断的加,不断的迭代。所以我感觉如果需要在迭代中增加元素,真正有用的还是CopyOnWriteArrayList,不过实际中,如果CopyOnWriteArrayList代价太高,可能我们可以申请一个临时list存放,在迭代后合并到主list中!

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;public class ConcurrentList {//private static List<String> TEST_LIST = new CopyOnWriteArrayList<String>();private static List<String> TEST_LIST = Collections.synchronizedList(new ArrayList<String>());public static void main(String[] args) {TEST_LIST.add("111");TEST_LIST.add("222");Iterator iterator  = TEST_LIST.iterator();  while(iterator.hasNext()){//System.out.println(iterator.next());TEST_LIST.add("333");System.out.println("add over");}  }
}

萌萌的IT人,IT人的乐园

转载于:https://www.cnblogs.com/yanghuahui/p/3365509.html

java 多线程操作List,已经做了同步synchronized,还会有ConcurrentModificationException,知道为什么吗?...相关推荐

  1. Java多线程02(线程安全、线程同步、等待唤醒机制)

    Java多线程2(线程安全.线程同步.等待唤醒机制.单例设计模式) 1.线程安全 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量 ...

  2. java多线程02-----------------synchronized底层实现及JVM对synchronized的优化

    java多线程02-----------------synchronized底层实现及JVM对synchronized的优化 提到java多线程,我们首先想到的就是synchronized关键字,它在 ...

  3. Java多线程(4)--线程的同步解决线程安全问题

    多线程出现安全问题 问题的原因: 当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误. 解决办法: 对多条操作共享数据的语 ...

  4. Java多线程系列(六):深入详解Synchronized同步锁的底层实现

    谈到多线程就不得不谈到Synchronized,很多同学只会使用,缺不是很明白整个Synchronized的底层实现原理,这也是面试经常被问到的环节,比如: synchronized的底层实现原理 s ...

  5. 基于Java多线程操作文件案例分享

    场景描述:某文件夹下有很多文件和文件夹,要求读取出该文件夹下的所有文件,并将文本打印输出. 案例分析:假设该文件夹下有10个文件,那么我们将所有的文件遍历一遍打印输出就可以了.但如果有100个, 10 ...

  6. Java 多线程(二)—— 线程的同步

     上文创建多线程买票的例子中注释会出现错票.重票的问题,本文来讲讲如何解决此问题.本文例子:利用多线程模拟 3 个窗口卖票 实现Runnable接口 public class TestThread2 ...

  7. java多线程操作同一资源

    一个购票系统某个票的总数是一个常数,购买者可以有多个,每个购买操作都会使总数减少. 我们设计一个多线程程序 public class ThreadDemo {public static void ma ...

  8. 编程开发之--java多线程学习总结(2)同步代码块

    1.第一种解决办法:同步代码块,关键字synchronized package com.lfy.ThreadsSynchronize;/*** 1.使用同步代码块* 语法:synchronized ( ...

  9. 实验八 java多线程操作_20182310实验八实验报告

    20182310 2019-2020 <数据结构与面向对象程序设计>实验八 树报告 课程:<程序设计与数据结构> 班级: 1823 姓名: 周烔 学号:20182310 实验教 ...

最新文章

  1. weblogic10重置控制台密码
  2. 用计算机写试卷反思,100分试卷反思怎么写
  3. boost::mp11::mp_iota相关用法的测试程序
  4. sklearn线性回归详解
  5. C#.NET 大型通用信息化系统集成快速开发平台 4.6 版本 - SSO单点登录接口
  6. 从月薪 1000 到 2W+,文科生如何逆袭成为大厂程序员?
  7. 认识虚拟化(virtualization)
  8. 【题解】狼和羊-C++
  9. Frame buffer分析 - fbmem.c【转】
  10. 如何在IDM官网安装IDM谷歌插件?
  11. 中文输入法中光标跟随能力触发的浏览器事件探究
  12. 09 自动发表博客评论
  13. word保留格式简体转换成繁体宏
  14. DOTA英雄 精美图片 大集合
  15. 有趣的兔子(斐波那契数列)
  16. 公安计算机专业就业前景,公安视听技术专业就业方向及就业前景分析
  17. mybatis-plus存数组对象,并从数据库查出这个数组
  18. 不敢“犯错”的应试教育
  19. 正在载入java_如何实现程序加载时弹出正在加载对话框
  20. ZooKeeper :Docker Compose部署ZooKeeper集群

热门文章

  1. linux chroot_Linux中chroot命令的实用指南
  2. jsf标签_JSF Facelet标签示例教程
  3. VS 母版使用配置技巧
  4. 详解SQL Server Profiler分析死锁几大步骤
  5. 盘点苹果乔布斯憎恨Google的十大原因
  6. Django 阅读笔记 - Cache
  7. 在PDA设备上安装和部署 SQL Server Compac 3.5(官方版)
  8. MOQL-复杂事件处理(CEP)
  9. Leetcode 276.栅栏涂色
  10. Matlab图形修饰之视点处理