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

ExecutorService处理ArrayList数据异常:部分未处理且重复执行最后元素

问题排查与修复

核心问题1:复用单例Runnable实例导致线程安全问题

你的ShopifyBulkSyncCoupons被标记为@Component,这意味着Spring会创建唯一的单例实例。在循环中你反复复用这个实例,不断调用setCouponId(id),而线程池中的线程可能还未开始执行前一个任务,此时id就被下一次循环覆盖了。这会直接导致:

  • 多个线程执行同一个id的任务
  • 部分id被直接跳过(被后续id覆盖)
  • 最终出现"仅第二、第三和最后一个元素被处理,最后一个反复执行"的现象

修复方案:每次循环创建新的任务实例

不要复用Spring注入的单例Bean,改为每次循环创建新的ShopifyBulkSyncCoupons实例,或者将该类改为原型模式(@Scope("prototype"))。

方案一:手动创建新实例

修改后的syncShopifyPriceRulesListToMongoV3方法:

public ResponseEntity<?> syncShopifyPriceRulesListToMongoV3(List<Long> ids) {
    logger.info("Start :: syncShopifyPriceRulesListToMongoV3 for ids size = "+ids.size());
    ExecutorService service = Executors.newFixedThreadPool(3);
    try {
        if (!ids.isEmpty()) {
            for (Long id : ids) {
                logger.info("id inside for"+id);
                // 每次创建新的任务实例,避免单例共享状态
                ShopifyBulkSyncCoupons task = new ShopifyBulkSyncCoupons();
                // 手动注入依赖(如果ShopifyBulkSyncCoupons依赖其他Spring Bean)
                task.setShopifyClient(shopifyClient);
                task.setProductAttrRepository(productAttrRepository);
                task.setPriceRuleDetailsRepo(priceRuleDetailsRepo);
                task.setCouponId(id);
                service.execute(task);
            }
        }
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    logger.info("End :: syncShopifyPriceRulesListToMongoV3");
    // 等待所有任务完成再返回,避免主线程提前结束导致任务异常
    try {
        service.shutdown();
        if (!service.awaitTermination(60, TimeUnit.SECONDS)) {
            service.shutdownNow();
        }
    } catch (InterruptedException e) {
        service.shutdownNow();
        Thread.currentThread().interrupt();
    }
    return new ResponseEntity<>(HttpStatus.OK);
}

方案二:将任务类改为原型作用域

修改ShopifyBulkSyncCoupons的作用域,让Spring每次返回新实例:

@Component
@Scope("prototype") // 每次获取都是新实例
public class ShopifyBulkSyncCoupons implements Runnable {
    // ... 原有代码
}

此时在循环中通过Spring容器获取新实例:

// 注入ApplicationContext
@Autowired
private ApplicationContext ctx;

// 循环中获取原型实例
ShopifyBulkSyncCoupons task = ctx.getBean(ShopifyBulkSyncCoupons.class);
task.setCouponId(id);
service.execute(task);

核心问题2:Run方法未处理异常,导致线程静默终止

你的run()方法没有捕获全局异常,如果shopifyClient.getPriceRuleById(CouponId)或数据库操作抛出异常,线程会直接终止且无日志输出,这会导致部分任务看起来未执行,甚至线程池线程数量减少,后续任务分配异常。

修复方案:添加全局异常捕获

@Override
public void run() {
    logger.info("Inside run for id "+CouponId);
    logger.info("ShopifyClientBean "+shopifyClient);
    try {
        PriceRuleService priceRuleService = new PriceRuleServiceImpl();

        ShopifyPriceRuleResponse priceRuleResponse = shopifyClient.getPriceRuleById(CouponId);
        if(null!=priceRuleResponse && null!=priceRuleResponse.getPriceRule()) {
            PriceRuleDetails priceRuleDetails = new PriceRuleDetails();
            List<Long> finalEntitledVariants = new ArrayList<>();
            priceRuleDetails = priceRuleService.mapShopifyPriceRuleToModel(priceRuleResponse.getPriceRule());
            if (!CollectionUtils.isEmpty(priceRuleDetails.getEntitledCollectionIds())) {
                List<Long> collectionIds = priceRuleDetails.getEntitledCollectionIds();
                List<ProductAttributes> prodAttr = productAttrRepository.findByCollectionsIn(collectionIds);
                for (ProductAttributes prod : prodAttr) {
                    finalEntitledVariants.add(prod.getVariantId());
                }
            }
            if (!CollectionUtils.isEmpty(priceRuleDetails.getEntitledProductIds())) {
                List<ProductAttributes> productAttributesList = productAttrRepository.findByProductIdIn(priceRuleDetails.getEntitledProductIds());
                for (ProductAttributes prod : productAttributesList) {
                    finalEntitledVariants.add(prod.getVariantId());
                }
            }
            if (!CollectionUtils.isEmpty(priceRuleDetails.getEntitledVariantIds())) {
                finalEntitledVariants.addAll(priceRuleDetails.getEntitledVariantIds());
            }
            priceRuleDetails.setEntitledVariantIds(finalEntitledVariants);
            priceRuleDetailsRepo.save(priceRuleDetails);
        }
    } catch (Exception e) {
        logger.error("Failed to process coupon id: "+CouponId, e);
        // 可根据业务需求添加重试、告警等逻辑
    }
}

额外优化:避免在run方法中创建Service实例

new PriceRuleServiceImpl()会每次创建新的Service实例,建议将PriceRuleService注入为Spring Bean,通过构造注入到ShopifyBulkSyncCoupons中,提升性能和可维护性:

@Component
@Scope("prototype")
public class ShopifyBulkSyncCoupons implements Runnable {
    private final PriceRuleService priceRuleService;
    private final ProductAttrRepository productAttrRepository;
    private final PriceRuleDetailsRepo priceRuleDetailsRepo;
    private final ShopifyClient shopifyClient;
    private Long couponId;

    // 构造注入所有依赖
    @Autowired
    public ShopifyBulkSyncCoupons(PriceRuleService priceRuleService,
                                  ProductAttrRepository productAttrRepository,
                                  PriceRuleDetailsRepo priceRuleDetailsRepo,
                                  ShopifyClient shopifyClient) {
        this.priceRuleService = priceRuleService;
        this.productAttrRepository = productAttrRepository;
        this.priceRuleDetailsRepo = priceRuleDetailsRepo;
        this.shopifyClient = shopifyClient;
    }

    public void setCouponId(Long couponId) {
        this.couponId = couponId;
    }

    @Override
    public void run() {
        logger.info("Inside run for id "+couponId);
        logger.info("ShopifyClientBean "+shopifyClient);
        try {
            ShopifyPriceRuleResponse priceRuleResponse = shopifyClient.getPriceRuleById(couponId);
            if(null!=priceRuleResponse && null!=priceRuleResponse.getPriceRule()) {
                PriceRuleDetails priceRuleDetails = priceRuleService.mapShopifyPriceRuleToModel(priceRuleResponse.getPriceRule());
                List<Long> finalEntitledVariants = new ArrayList<>();
                if (!CollectionUtils.isEmpty(priceRuleDetails.getEntitledCollectionIds())) {
                    List<Long> collectionIds = priceRuleDetails.getEntitledCollectionIds();
                    List<ProductAttributes> prodAttr = productAttrRepository.findByCollectionsIn(collectionIds);
                    prodAttr.forEach(prod -> finalEntitledVariants.add(prod.getVariantId()));
                }
                if (!CollectionUtils.isEmpty(priceRuleDetails.getEntitledProductIds())) {
                    List<ProductAttributes> productAttributesList = productAttrRepository.findByProductIdIn(priceRuleDetails.getEntitledProductIds());
                    productAttributesList.forEach(prod -> finalEntitledVariants.add(prod.getVariantId()));
                }
                if (!CollectionUtils.isEmpty(priceRuleDetails.getEntitledVariantIds())) {
                    finalEntitledVariants.addAll(priceRuleDetails.getEntitledVariantIds());
                }
                priceRuleDetails.setEntitledVariantIds(finalEntitledVariants);
                priceRuleDetailsRepo.save(priceRuleDetails);
            }
        } catch (Exception e) {
            logger.error("Failed to process coupon id: "+couponId, e);
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 08:27:13