多线程修改了ArrayList , 如何知道被谁修改的?

发布于 2022-09-11 16:51:27 字数 433 浏览 19 评论 0

public void method(){
    ...
    for(Object obj: finalObjects){
        ....
        doSth(obj);
        ....
    }
}

我在主线程遍历 ArrayList 的时候,发生了 java.util.ConcurrentModificationException

已检查 doSth 并没有修改 全局变量 finalObjects,所以我怀疑是某个异步方法在我遍历期间修改了这个变量,由于系统庞大,没法直接查找哪里修改了它,有没有办法在运行时获取,是哪个地方的异步,或者是哪个线程,修改了 finalObjects 导致异常发生?

请教各位大佬 ,谢谢 !!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

我的影子我的梦 2022-09-18 16:51:28

有完整的程序吗,我可以帮你分析

只是偏爱你 2022-09-18 16:51:28

有完整的程序吗,可以帮你分析

仙女 2022-09-18 16:51:28

世界是科学的,先看下finalObject是在哪里的,被使用的地方有哪些,另外哪里有额外的线程在跑,你需要知道,然后一个一个的排查. 你这点信息,塞牙缝都不够

心的位置 2022-09-18 16:51:28

最优解决方案:
Vector 替换 ArrayList

“我偏不”的解决方案:
出现 java.util.ConcurrentModificationException
是因为在 for(X : Xs) 的循环执行过程中,迭代器检测到元素数目有变化
因此,换成 for(int i; i <; i++) 就可以避免这个异常了(当然,如果删除了元素就有空指针问题)

“让一个方法独占cpu直到执行完”,这描述……
我理解应该是这么个意思吧:

public void method(){
    synchronized(finalObjects){
      //……
    }
}

似乎表面上是可以解决问题了
但是每发现个问题都得加个 synchronized,所以,为啥不直接用 Vector

星光不落少年眉 2022-09-18 16:51:28
public void serverTick()
{
    List worldServers = new ArrayList(MinecraftServer.getServer().worldServers)
    for(WorldServer world : MinecraftServer.getServer().worldServers)
    {
        List playerEntities = new ArrayList(world.playerEntities)
        for(Object player : playerEntities)
        {
            getPlayerData((EntityPlayer)player).tick((EntityPlayer)player);
        }
    }
}

你应该向如上的方式修改你的代码。而不是直接依赖某个对象的属性,最好的做法应该是提供 get 方法在方法的内部返回时拷贝一个新的 list,这样可以保证属性的安全性,保证外部的修改不会影响到内部的属性。

因你目前的实现依赖一个对象的属性,你无法保证它不会被其它地方修改,所以你应该要修改你的循环实现方式。

吻泪 2022-09-18 16:51:28

多庞大? 可以尝试 用ide的全局搜索 查查代码看看

第几種人 2022-09-18 16:51:28

针对这个问题 LZ 可以深入研究一下 ConcurrentModificationException 产生的原因:
java 的增强 for 循环默认使用的 iterator 迭代器的 hasNext 和 next 来遍历元素。这样便不难理解这个问题的出现原因了。
你在其他地方执行了 remove 或者 add 方法,这个方法只会修改 modCount 值不会修改 expectedModCount 的内容,所以在迭代器调用 next 时候校验就会抛出异常

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

所以问题找到了解决这个问题就简单了
方案一
如果是 remove 操作,找到你其他地方remove,修改成迭代器的方式

ArrayList<Integer> list = new ArrayList<Integer>();
list.add(2);
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
    Integer integer = iterator.next();
    if(integer==2)
        iterator.remove();   //注意这个地方
}

方案二
直接使用 CopyOnWriteArrayList 替换掉你的 ArrayList,前者在add 和 remove 时候都使用了 lock,很好的避免了这个问题。


小编为订阅号「码匠笔记」号主,先后就职于 ThoughtWorks、阿里巴巴等互联网公司的经验分享,包含但不限于 JAVA、并发编程、性能优化、架构设计、小程序、开源软件等。有兴趣可以关注一波,一起学习、讨论。

老旧海报 2022-09-18 16:51:27

很感谢各位的回答。只是解决这个问题,如@kevinz 那样,就可以了。
但是我的目的是想跟踪这个变量在这一段代码执行过程中,还有哪个线程修改了它。不过看来是很困难,我是一点头绪都没有。

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文