使用ExecutorService的submit方法处理列表记录仅最后一条生效如何解决
问题根因
你遇到的问题核心是重复使用了同一个Runnable实例:通过@Autowired注入的msnsfExecutorThread默认是Spring单例对象,整个循环过程中你反复给同一个对象的属性赋值,前一次赋值的结果会被后一次直接覆盖。等到线程池真正调度执行这个对象的run方法时,对象属性早就被改成了最后一条记录的值,自然只能读取到最后一条数据。
解决方案
方案1:每次循环创建新的Runnable实例
如果MsnsfExecutorThread不需要依赖Spring注入其他Bean,直接每次循环new新实例即可:
ExecutorService executor = Executors.newFixedThreadPool(3); for (TempMSISDNCollectFee tempMSISDNCollectFee : list) { // 每次循环创建独立的执行对象,属性互不干扰 MsnsfExecutorThread msnsfExecutorThread = new MsnsfExecutorThread(); msnsfExecutorThread.setGcsAccountId(tempMSISDNCollectFee.getGcsAccountId()); msnsfExecutorThread.setMsisdn(tempMSISDNCollectFee.getMsisdn()); msnsfExecutorThread.setProcessStatus(tempMSISDNCollectFee.getProcessingStatus()); msnsfExecutorThread.setPartnerCode(tempMSISDNCollectFee.getPartnerCode()); executor.submit(msnsfExecutorThread); }
如果MsnsfExecutorThread内部需要注入Spring容器中的其他Bean,无法直接new,可以将MsnsfExecutorThread的作用域改为原型模式,每次从Spring上下文获取新实例:
- 给
MsnsfExecutorThread类增加原型作用域注解:
@Component @Scope("prototype") public class MsnsfExecutorThread implements Runnable { // 原有类逻辑保持不变 }
- 在使用类中注入
ApplicationContext,每次循环获取新实例:
@Autowired private ApplicationContext applicationContext; // 执行逻辑部分 ExecutorService executor = Executors.newFixedThreadPool(3); for (TempMSISDNCollectFee tempMSISDNCollectFee : list) { // 从Spring上下文获取全新的实例 MsnsfExecutorThread msnsfExecutorThread = applicationContext.getBean(MsnsfExecutorThread.class); msnsfExecutorThread.setGcsAccountId(tempMSISDNCollectFee.getGcsAccountId()); msnsfExecutorThread.setMsisdn(tempMSISDNCollectFee.getMsisdn()); msnsfExecutorThread.setProcessStatus(tempMSISDNCollectFee.getProcessingStatus()); msnsfExecutorThread.setPartnerCode(tempMSISDNCollectFee.getPartnerCode()); executor.submit(msnsfExecutorThread); }
方案2:改用局部变量传递参数
如果不想每次创建新的MsnsfExecutorThread实例,可以修改执行逻辑,不依赖成员变量存储参数,改用局部变量捕获每次循环的参数值:
ExecutorService executor = Executors.newFixedThreadPool(3); for (TempMSISDNCollectFee tempMSISDNCollectFee : list) { // 提前提取当前循环的参数作为局部变量,lambda会捕获保留当前值 String gcsAccountId = tempMSISDNCollectFee.getGcsAccountId(); String msisdn = tempMSISDNCollectFee.getMsisdn(); Integer processStatus = tempMSISDNCollectFee.getProcessingStatus(); String partnerCode = tempMSISDNCollectFee.getPartnerCode(); executor.submit(() -> { // 直接调用处理方法,把参数传入,不需要修改成员变量 msnsfExecutorThread.process(gcsAccountId, msisdn, processStatus, partnerCode); }); }
同时给MsnsfExecutorThread新增接收参数的process方法,原有run方法的逻辑迁移到process方法中即可。
内容的提问来源于stack exchange,提问作者mamoon
相关产品推荐
相关产品推荐

