线上问题整理-ConcurrentModificationException异常

项目场景:

商品改价:商品改价中通过多线程批量处理经过 Lists.partition拆分的集合对象


问题描述

商品改价中通过多线程批量处理经过 Lists.partition拆分的集合对象,发现偶尔会报

java.util.ConcurrentModificationException: null
        at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1231)
        at java.util.ArrayList$SubList.spliterator(ArrayList.java:1235)
        at java.util.Collection.stream(Collection.java:581)

代码实现逻辑

 for (Map.Entry<Integer, List<PriceHandle>> partnerIdProductId : priceHandleGroup.entrySet()) {
            Integer partnerId = partnerIdProductId.getKey();
            List<PriceHandle> priceHandles = partnerIdProductId.getValue();
            log.info("当前分片={},开始同步partnerId={} size={}", shardingItem, partnerId, priceHandles.size());
            List<List<PriceHandle>> pidsGroup = Lists.partition(priceHandles, splitSize);
            log.info("partnerId={},分为{}组,每组{}条", partnerId, pidsGroup.size(), splitSize);
            CountDownLatch countDownLatch = new CountDownLatch(pidsGroup.size());
            for (List<PriceHandle> priceHandlesLittle : pidsGroup) {
                executorService.submit(() -> {
                    try {
                        List<Long> productIds = priceHandlesLittle.stream().map(PriceHandle::getProductId).collect(Collectors.toList());
                        List<ProductMapEntity> productMapEntities = productMappingAllPartnerService.getProductMapListByPartnerIdAndProductIds(partnerId, productIds);
                        //没有映射的失败不处理
                        List<Long> mapProductIds = productMapEntities.stream().map(ProductMapEntity::getProductId).collect(Collectors.toList());
                        List<PriceHandle> noMapPriceHandles = priceHandlesLittle.stream().filter(e -> !mapProductIds.contains(e.getProductId())
                        ).collect(Collectors.toList());
                        if (CollectionUtils.isNotEmpty(noMapPriceHandles)) {
                            jingdongPriceSyncService.updateBatch(noMapPriceHandles, EventStatus.HANDLED_NO_NEED.eventStatus, "商品无映射");
                        }
                        priceHandlesLittle.removeAll(noMapPriceHandles);

                        // 过滤productPool中不存在的重复品
                        filterRepeatProductId(productMapEntities, priceHandlesLittle);
                        if (CollectionUtils.isEmpty(productMapEntities) || CollectionUtils.isEmpty(priceHandlesLittle)) {
                            log.info("过滤productPool中不存在的重复品");
                            return;
                        }
                        PriceSyncService.synPrice(partnerId, productMapEntities, false, priceHandlesLittle);
                    } catch (Exception e) {
                        log.error("同步价格异常,当前处理的店铺:{}", partnerId, e);
                    } finally {
                        countDownLatch.countDown();
                    }
                });
            }
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                log.error("countDownLatch.await error", e);
                Thread.currentThread().interrupt();
            }

        }

原因分析:

这里需要注意的是在使用Google Guava库中的Lists.partition方法时,如果对原始列表进行了修改,则可能会导致ConcurrentModificationException异常的抛出。这是因为Lists.partition方法返回的是原始列表的视图,而不是副本。因此,如果在对视图进行操作时修改了原始列表,则会导致ConcurrentModificationException异常的抛出。
因此在我们的这段代码中 问题在于又通过拆分后的subList做了removeAll的操作


具体复现
在这里插入图片描述

解决方案:

方法1不要再for循环中基于视图 修改修改原始列表
方法2 在for循环中新建一个list对象来包装原来的list
具体实现代码

for (Map.Entry<Integer, List<PriceHandle>> partnerIdProductId : priceHandleGroup.entrySet()) {
            Integer partnerId = partnerIdProductId.getKey();
            List<PriceHandle> priceHandles = partnerIdProductId.getValue();
            log.info("当前分片={},开始同步partnerId={} size={}", shardingItem, partnerId, priceHandles.size());
            List<List<PriceHandle>> pidsGroup = Lists.partition(priceHandles, splitSize);
            log.info("partnerId={},分为{}组,每组{}条", partnerId, pidsGroup.size(), splitSize);
            CountDownLatch countDownLatch = new CountDownLatch(pidsGroup.size());
            for (List<PriceHandle> priceHandlesLittle : pidsGroup) {
                // 由于存在并发remove,需要重新赋值新ArrayList,防止ArrayList$SubList引起的ConcurrentModificationException
                List<PriceHandle> copyPriceHandlesLittle = new ArrayList<>(priceHandlesLittle);
                executorService.submit(() -> {
                    try {
                        List<Long> productIds = copyPriceHandlesLittle.stream().map(PriceHandle::getProductId).collect(Collectors.toList());
                        List<ProductMapEntity> productMapEntities = productMappingAllPartnerService.getProductMapListByPartnerIdAndProductIds(partnerId, productIds);
                        //没有映射的失败不处理
                        List<Long> mapProductIds = productMapEntities.stream().map(ProductMapEntity::getProductId).collect(Collectors.toList());
                        List<PriceHandle> noMapPriceHandles = copyPriceHandlesLittle.stream().filter(e -> !mapProductIds.contains(e.getProductId())
                        ).collect(Collectors.toList());
                        if (CollectionUtils.isNotEmpty(noMapPriceHandles)) {
                            PriceSyncService.updateBatch(noMapPriceHandles, EventStatus.HANDLED_NO_NEED.eventStatus, "商品无映射");
                        }
                        copyPriceHandlesLittle.removeAll(noMapPriceHandles);

                        // 过滤productPool中不存在的重复品
                        filterRepeatProductId(productMapEntities, copyPriceHandlesLittle);
                        if (CollectionUtils.isEmpty(productMapEntities) || CollectionUtils.isEmpty(copyPriceHandlesLittle)) {
                            log.info("过滤productPool中不存在的重复品");
                            return;
                        }
                        PriceSyncService.synJPrice(partnerId, productMapEntities, false, copyPriceHandlesLittle);
                    } catch (Exception e) {
                        log.error("同步价格异常,当前处理的店铺:{}", partnerId, e);
                    } finally {
                        countDownLatch.countDown();
                    }
                });
            }
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                log.error("countDownLatch.await error", e);
                Thread.currentThread().interrupt();
            }

        }


参考
https://blog.csdn.net/weixin_48321825/article/details/121012733?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0-121012733-blog-102456357.235v38pc_relevant_default_base3&spm=1001.2101.3001.4242.1&utm_relevant_index=3

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/193902.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【密码学】【多方安全计算】不经意传输(Oblivious Transfer,OT)

文章目录 不经意传输&#xff08;oblivious transfer&#xff09;定义不经意传输的实例&#xff08;1 out 2&#xff0c;二选一不经意传输&#xff09;基于RSA的1 out 2 不经意传输疑问 不经意传输&#xff08;oblivious transfer&#xff09;定义 不经意传输&#xff08;obli…

开源运维监控系统-Nightingale(-夜莺)应用实践(未完)

一、前言 某业务系统因OS改造,原先的Zabbix监控系统推倒后未重建,本来计划用外部企业内其他监控系统接入,后又通知需要自建才能对接,考虑之前zabbix的一些不便,本次计划采用一个类Prometheus的监控系统,镜调研后发现Nightingale兼容Prometheus,又有一些其他功能增强,又…

【算法每日一练]-图论(保姆级教程 篇6(图上dp))#最大食物链 #游走

目录 题目&#xff1a;最大食物链 解法一&#xff1a; 解法二&#xff1a; 记忆化 题目&#xff1a;游走 思路&#xff1a; 题目&#xff1a;最大食物链 解法一&#xff1a; 我们标记f[i]是被f[x]捕食的点对应的类食物链数 不难得出&#xff1a; f[x]∑(f[i]) 首先从生…

Final project COMP 424, McGill University

Final project COMP 424, McGill University WeChat: zh6-86

电脑如何定时关机?

电脑如何定时关机&#xff1f;我承认自己是个相当粗心的人&#xff0c;尤其是在急于离开时经常会忘记关闭电脑&#xff0c;结果就是电量耗尽&#xff0c;导致电脑自动关机。而且&#xff0c;在我使用电脑的时候&#xff0c;经常需要进行软件下载、更新等任务。如果我一直坐等任…

XIAO ESP32S3之套件简绍

很高兴收到柴火创客空间寄来的XIAO ESP32S3开发套件。 一、套件介绍 1、电路板部分 一块XIAO ESP32S3主板、一块摄像头接口板&#xff08;可接SD卡&#xff09;&#xff0c;一根2.4G天线。 2、配件部分 一根USB-A转TypeC数据线、一个USB3.0转TypeC转接头、一个SD卡读卡器&am…

vue实战——登录【详解】(含自适配全屏背景,记住账号--支持多账号,显隐密码切换,登录状态保持)

效果预览 技术要点——自适配全屏背景 https://blog.csdn.net/weixin_41192489/article/details/119992992 技术要点——密码输入框 自定义图标切换显示隐藏 https://blog.csdn.net/weixin_41192489/article/details/133940676 技术要点——记住账号&#xff08;支持多账号&…

【WP】Geek Challenge 2023 web 部分wp

EzHttp http协议基础题 unsign 简单反序列化题 n00b_Upload 很简单的文件上传&#xff0c;上传1.php&#xff0c;抓包&#xff0c;发现php内容被过滤了&#xff0c;改为<? eval($_POST[‘a’]);?>&#xff0c;上传成功&#xff0c;命令执行读取就好了 easy_php …

Docker+Jmeter+InfluxDB+Grafana优化压测报告

1、安装docker 运行Docker&#xff0c;并记录当前Docker的IP地址&#xff0c;本处IP为192.168.99.100 2、安装并配置influxDB 下载镜像 网上获取&#xff1a;docker pull tutum/influxdb 本地安装&#xff1a;docker load < influxdb.tar 安装influxDB容器 docker run…

尚硅谷大数据项目《在线教育之实时数仓》笔记008

视频地址&#xff1a;尚硅谷大数据项目《在线教育之实时数仓》_哔哩哔哩_bilibili 目录 第10章 数仓开发之DWS层 P066 P067 P068 P069 P070 P071 P072 P073 P074 P075 P076 P077 P078 P079 P080 P081 P082 第10章 数仓开发之DWS层 P066 第10章 数仓开发之DW…

Java游戏 王者荣耀

GameFrame类 所需图片&#xff1a; package 王者荣耀;import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.File; import java.util.ArrayList…

2023年亚太杯APMCM数学建模大赛A题水果采摘机器人的图像识别

2023年亚太杯APMCM数学建模大赛 A题 水果采摘机器人的图像识别 原题再现 中国是世界上最大的苹果生产国&#xff0c;年产量约3500万吨。同时&#xff0c;中国也是世界上最大的苹果出口国&#xff0c;世界上每两个苹果中就有一个是中国出口的&#xff0c;世界上超过六分之一的…

Docker-简介、基本操作

目录 Docker理解 1、Docker本质 2、Docker与虚拟机的区别 3、Docker和JVM虚拟化的区别 4、容器、镜像的理解 5、Docker架构 Docker客户端 Docker服务器 Docker镜像 Docker容器 镜像仓库 Docker基本操作 1、Docker镜像仓库 镜像仓库分类 镜像仓库命令 docker lo…

CV计算机视觉每日开源代码Paper with code速览-2023.11.22

点击CV计算机视觉&#xff0c;关注更多CV干货 论文已打包&#xff0c;点击进入—>下载界面 点击加入—>CV计算机视觉交流群 1.【语义分割】Mobile-Seed: Joint Semantic Segmentation and Boundary Detection for Mobile Robots 论文地址&#xff1a;https://arxiv.or…

高效视频剪辑:按指定时长批量分割视频,释放无尽创意

随着数字媒体技术的不断发展&#xff0c;视频剪辑已经成为日常生活中不可或缺的一部分。无论是制作电影、电视剧&#xff0c;还是创意生活短视频&#xff0c;视频剪辑都扮演着重要的角色。然而&#xff0c;对于许多非专业人士来说&#xff0c;视频剪辑可能是一项复杂而耗时的任…

C#,《小白学程序》第二十五课:大数乘法(BigInteger Multiply)的Karatsuba算法及源代码

1 文本格式 /// <summary> /// 《小白学程序》第二十五课&#xff1a;大数&#xff08;BigInteger&#xff09;的Karatsuba乘法 /// Multiplies two bit strings X and Y and returns result as long integer /// </summary> /// <param name"a">&…

如何在Ubuntu系统上安装Redis

Redis的下载 Redis安装包分为windows版和Linux版当前示例中介绍的是Linux版本Linux的下载地址&#xff1a;Index of /releases/ (redis.io)本次下载的压缩包为&#xff1a;redis-6.2.14.tar.gzRedis的安装 将压缩包通过ssh远程工具上传到Linux服务器中解压压缩包 tar -zxvf red…

深度学习18

卷积层 查看每个数据 使用tensorboard查看 池化层 使用数据集进行训练 创建实例&#xff0c;使用tensorboard进行显示 最大池化保留了图片信息&#xff0c;神经网络训练的数据量大大减小&#xff0c;可以加快训练 非线性激活 非线性激活为神经网络加入了一些非线性的特质…

蓝桥杯每日一题2023.11.27

题目描述 星系炸弹 - 蓝桥云课 (lanqiao.cn) 题目分析 对于此题目一一枚举即可 #include<bits/stdc.h> using namespace std; bool is_r(int n) {if((n % 4 0 && n % 100 ! 0)|| n % 400 0)return true;return false; } int mm[13] {0, 31, 28, 31, 30, 3…

【日常总结】优雅升级Swagger 2 升至 3.0, 全局设置 content-type application/json

目录 一、场景 二、问题 三、解决方案 四、延伸 上一节&#xff1a;【日常总结】Swagger-ui 导入 showdoc &#xff08;优雅升级Swagger 2 升至 3.0&#xff09;-CSDN博客 一、场景 接上一节&#xff1a;在 Swagger3Config extends WebMvcConfigurationSupport&#xff0c…
最新文章