线上问题整理-ConcurrentModificationException异常

news2024/10/6 18:27:08

项目场景:

商品改价:商品改价中通过多线程批量处理经过 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.coloradmin.cn/o/1260806.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue3 + Scss 实现主题切换效果

Vue3 Scss 实现主题切换效果 先给大家看一下主题切换的效果&#xff1a; 像这样的效果实现起来并不难&#xff0c;只是比较麻烦&#xff0c;目前我知道的有两种方式可以实现&#xff0c;分别是 CSS 变量、样式文件切换&#xff0c;下面是该效果的核心实现方法 CSS变量 给…

使用Docker compose方式安装Spug,并结合内网穿透实现远程访问

文章目录 前言1. Docker安装Spug2 . 本地访问测试3. Linux 安装cpolar4. 配置Spug公网访问地址5. 公网远程访问Spug管理界面6. 固定Spug公网地址 前言 Spug 面向中小型企业设计的轻量级无 Agent 的自动化运维平台&#xff0c;整合了主机管理、主机批量执行、主机在线终端、文件…

【数据结构初阶】单链表

各位读者老爷好&#xff0c;鼠鼠我又来了哈。鼠鼠我呀现在来基于C语言实现以下单链表&#xff0c;希望对你有所帮助&#xff01; 目录 1.链表的概念及结构 2.链表的分类 3.无头单向非循环链表的实现 3.1.单链表打印 3.2.单链表尾插 3.3.单链表头插 3.4.单链表尾删 3.5…

Idea空白目录自动折叠的问题

IDEA创建空白项目和文件夹会自动折叠的问题。 有时文件项目会自动折叠&#xff0c;折叠后&#xff0c;不仅不好找项目和文件&#xff0c;还容易造成特别低端的错误。 如图&#xff1a; 当我们要在example目录下创建文件时&#xff0c;很容易就在springgaopdemo下创建了。 因为…

正则表达式 通配符 awk文本处理工具

目录 什么是正则表达式 概念 正则表达式的结构 正则表达式的组成 元字符 元字符点&#xff08;.&#xff09; 代表字符. 点值表示点需要转义 \ r..t 代表r到t之间任意两个字符 过滤出小写 过滤出非小写 space空格 [[:space:]] 表示次数 位置锚定 例&#xff1a…

笔记十九*、选中高亮和嵌套路由使用

19.1 选中高亮 NavLink App.jsx import React from "react"; import {NavLink, useRoutes} from "react-router-dom"; import routes from "./routes/index.jsx"; import "./app.css"const App () > {const element useRoutes(…

“文件批量改名专家:轻松自定义重命名并智能导出文件信息“

在日常工作中&#xff0c;处理大量文件时&#xff0c;往往需要一款得力的文件批量改名工具来协助我们高效、有序地进行文件管理。今天&#xff0c;我要向大家介绍一款强大的文件批量改名工具&#xff0c;它不仅支持统一自定义重命名&#xff0c;还能将相关信息导出到表格中&…

一、Oceanbase基础

一、集群相关概念 集群&#xff1a;整个分布式数据库。Region&#xff1a;表示区域&#xff0c;是地域的逻辑概念&#xff0c;如1个城市&#xff0c;1个集群可以有多个Region&#xff0c;用于跨城市远 距离容灾。Zone&#xff1a;表示分区&#xff0c;是机房或机架的逻辑概念…

[PyTorch][chapter 1][李宏毅深度学习-AI 简介]

前言&#xff1a; 李宏毅深度学习从2017-2023的系列课程总结 内容 章节 强化学习 11 李宏毅机器学习 【2017】 40 李宏毅机器学习深度学习(完整版)国语 【2020】 119 李宏毅大佬的深度学习与机器学【2022】 90 李宏毅机器学习完整课程【2023】 43 总结 303 目录…

lenovo联想笔记本YogaPro 14s IRP8D 2023款(83BU)原装出厂Windows11预装OEM系统

链接&#xff1a;https://pan.baidu.com/s/1s7PcN-y8RyHSV7uJQzC5OQ?pwddy9y 提取码&#xff1a;dy9y 联想电脑原厂W11系统&#xff0c;自带所有驱动、出厂主题壁纸、系统属性专属LOGO标志、Office办公软件、联想电脑管家等预装程序 所需要工具&#xff1a;16GB或以上的U盘…

百度手机浏览器关键词排名优化——提升关键词排名 开源百度小程序源码系统 附带完整的搭建教程

百度作为国内领先的搜索引擎&#xff0c;一直致力于为用户提供最优质的信息服务。在移动互联网时代&#xff0c;手机浏览器成为了用户获取信息的主要渠道。而小程序作为轻量级的应用程序&#xff0c;具有即用即走、无需下载等优势&#xff0c;越来越受到用户的青睐。然而&#…

C语言第三十四弹--矩形逆置

C语言实现矩阵逆置 逆置结果如图 思路&#xff1a;通过观察逆置结果&#xff0c;首先发现行数和列数都发生了调换。其次观察逆置前后数字对应的下标&#xff0c;逆置前数字对应下标为:[x][j] 逆置后数字对应下标为&#xff1a;[y][x]。综上&#xff0c;就可以实现矩阵逆置。 …

ChromeDriver最新版本下载与安装方法

关于ChromeDriver最新下载地址&#xff1a;https://googlechromelabs.github.io/chrome-for-testing/ 下载与安装 setp1&#xff1a;查看Chrome浏览器版本 首先&#xff0c;需要检查Chrome浏览器的版本。请按照以下步骤进行&#xff1a; 打开Chrome浏览器。 点击浏览器右上角…

设计模式—迪米特原则(LOD)

1.背景 1987年秋天由美国Northeastern University的Ian Holland提出&#xff0c;被UML的创始者之一Booch等普及。后来&#xff0c;因为在经典著作《 The Pragmatic Programmer》而广为人知。 2.概念 迪米特法则&#xff08;Law of Demeter&#xff09;又叫作最少知识原则&…

14 网关实战:网关聚合API文档

上节课介绍了网关层的认证鉴权,今天这节介绍一下网关层如何聚合API接口文文档。 为什么需要聚合API接口文档? 大型微服务系统模块众多,木谷博客系统就有9个,如果这些服务的接口地址没有一个统一,那么客户端将要保存每个服务的接口地址,这个肯定是不现实。 先来看一下A…

小航助学题库蓝桥杯题库stem选拔赛(22年3月)(含题库教师学生账号)

需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09;_程序猿下山的博客-CSDN博客 需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09;_程序猿下山的博客-CSD…

Mo0n(月亮) MCGS触摸屏在野0day利用,强制卡死锁屏

项目:https://github.com/MartinxMax/Mo0n 后面还会不会在,我可就不知道了奥…还不收藏点赞关注 扫描存在漏洞的设备 #python3 Mo0n.py -scan 192.168.0.0/24 入侵锁屏 #python3 Mo0n.py -rhost 192.168.0.102 -lock 解锁 #python3 Mo0n.py -rhost 192.168.0.102 -unlock …

Jetpack Compose中适应性布局的新API

Jetpack Compose中适应性布局的新API 针对大屏幕优化的新组合件。 使用新的Material适应性布局&#xff0c;为手机、可折叠设备和平板电脑构建应用程序变得更加简单&#xff01;市场上各种不同尺寸的Android设备的存在挑战了构建应用程序时对屏幕尺寸的通常假设。开发者不应该…

什么是动态住宅IP?它有什么用途?

随着网络的迅速发展&#xff0c;许多人对代理IP已经有了比较深刻的认识&#xff0c;并且广泛地运用到了各自的业务中&#xff0c;尤其在跨境的相关业务中表现尤其卓越。对于代理IP的类别&#xff0c;也需要根据自己的业务类型具体选择最合适的&#xff0c;那么今天IPFoxy就给大…

TS 函数及多态

TS 能推导出函数体中的类型&#xff0c;但多数情况下无法推导出参数的类型&#xff0c;只有少数特殊情况下能根据上下文推导参数的类型。返回类型能推导出&#xff0c;不过也可以显式注解。 1 声明和调用函数 一般来说&#xff0c;在方法中的this值为调用该方法时位于点号左侧…