You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ForkJoinTask.join()调用偶发ConcurrentModificationException排查

问题背景

基于ForkJoinTask实现爬虫的页面链接扫描、递归爬取逻辑时,调用.join()方法收集子任务执行结果偶发ConcurrentModificationException异常,异常栈指向join调用行,尝试用ReentrantLock包裹join相关代码段未解决问题。

关联实现代码
Spider[] newSpiders = crawlForURLs("a", "href").stream()
        .filter(url -> SpiderUtils.isLinkURLValid(url))
        .map(url -> new Spider(url).fork()).toArray(Spider[]::new);

for (Spider c : newSpiders) {
    Image[] crawledImages = c.join();
}
异常对应代码位置
  • com.eulerity.hackathon.imagefinder.Spider.compute (Spider.java:85):对应代码为Image[] crawledImages = c.join();
  • com.eulerity.hackathon.imagefinder.ImageFinder.doPost (ImageFinder.java:45):对应代码为images = SpiderUtils.commonPool.invoke(baseSpider);
根因分析

ConcurrentModificationException指向join行不代表异常由join方法本身抛出,加锁无效是因为锁的覆盖范围完全错误,核心触发逻辑如下:

  • 该异常是集合fail-fast机制的标准表现:遍历非线程安全集合时,集合内部维护的修改计数(modCount)和遍历开始时记录的预期值不一致,即遍历过程中集合被执行了新增/删除等结构修改操作。ForkJoinPool的工作窃取机制会让fork()提交的子任务立刻由池内工作线程并行执行,不会等代码走到join()逻辑才开始运行。
  • 最高频触发点:crawlForURLs返回的是全局共享的非线程安全集合(比如爬虫全局维护的已爬链接集合、待爬队列),在对该集合执行stream().filter().map()遍历的同时,已经fork出去的子任务正在修改这个共享集合,直接触发异常。仅给后续join循环加锁,完全没覆盖前面stream遍历集合、子任务修改集合的代码路径,起不到互斥作用。
  • 次高频触发点:多个Spider子任务同时往同一个普通ArrayList/HashSet/HashMap这类非线程安全集合写入爬取结果、已访问标记,当join触发当前线程帮着执行其他子任务时,就可能在遍历集合的过程中碰到其他线程修改集合结构的情况,抛出异常。
修复方案
  • 替换所有跨ForkJoinTask共享的非线程安全集合:已访问URL集合用ConcurrentHashMap.newKeySet()实现;如果需要多任务共同汇总爬取结果,要么用Collections.synchronizedList包装集合,要么让每个子任务返回独立的结果数组,等所有子任务join完成后再在当前线程统一合并,禁止多个子任务同时往同一个普通非线程安全集合写数据。
  • 调整crawlForURLs返回逻辑:不要直接返回全局共享的链接集合,每次爬取完当前页面的链接后,返回当前页面独立的集合快照(比如new ArrayList<>(当前页爬取到的临时链接集合)),保证stream遍历的集合是当前任务私有的,不会被其他并行任务修改。
  • 遍历集合时避免结构修改:如果确实需要在遍历过程中调整集合内容,不要直接用for-each循环遍历原集合,要么用迭代器提供的remove()方法操作,要么遍历前先生成集合快照(比如new ArrayList<>(原集合)),遍历快照对象,从根源避免遍历过程中集合结构被改动。
  • 锁使用修正:如果选择用互斥锁保护共享状态,必须把所有读、写共享集合的代码路径全放在同一个锁的保护范围内,不能只锁join片段,否则锁完全不会生效。

内容的提问来源于stack exchange,提问作者WubbaLubbaDubbDubb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 02:54:37