Spring Boot中如何避免@Async方法参数列表被清空?
问题:Spring Boot @Async异步批量插入时列表被清空导致数据丢失
在CSV数据导入数据库的场景中,每积累10000条记录就调用标注了@Async的addInputDataStagingV12方法,调用后主线程立即清空stagingData列表,结果异步方法中的列表也被清空,无法插入数据;移除@Async注解后功能正常。
相关代码
主方法所在类
@Async("threadPoolTaskExecutor") @Override public CommonResponse generateDedupInputFile(String inputRequest){ if (stagingData.size() == 10000) { futures.add(databaseUpdateChanges.addInputDataStagingV12("tbl_staging", fieldQuery, config.getInputFields(), batchId, stagingData)); logger.info("staging size: {}", stagingData.size()); stagingData.clear(); } }
DB操作类
@Async("threadPoolTaskExecutor") @Transactional public Future<String> addInputDataStagingV12(String tableName, String fieldQuery, List<InputField> inputFields, BigInteger batchId, List<StagingDataModel> stagingDataModels) { logger.info("Size : {}", stagingDataModels.size()); // 此处输出大小为0 /** 数据插入查询逻辑 **/ }
问题原因
Java中List是引用类型,主线程调用异步方法时传递的是stagingData的引用,而非副本。异步方法在后台线程执行时,主线程已经调用了stagingData.clear(),导致异步方法持有的同一个引用指向的列表内容被清空。
同步调用时,addInputDataStagingV12方法会执行完成后才会执行clear(),因此不会出现问题。
解决方案
方案1:传递列表副本(推荐)
在调用异步方法时,创建stagingData的副本传递进去,这样异步方法操作的是独立的列表,主线程清空原列表不会影响异步任务:
if (stagingData.size() == 10000) { // 创建副本传递给异步方法 futures.add(databaseUpdateChanges.addInputDataStagingV12("tbl_staging", fieldQuery, config.getInputFields(), batchId, new ArrayList<>(stagingData))); logger.info("staging size: {}", stagingData.size()); stagingData.clear(); }
方案2:新建列表替代清空操作
每次批量提交后,直接创建新的列表替换原stagingData,原列表的引用被异步方法持有,不会被清空:
if (stagingData.size() == 10000) { futures.add(databaseUpdateChanges.addInputDataStagingV12("tbl_staging", fieldQuery, config.getInputFields(), batchId, stagingData)); logger.info("staging size: {}", stagingData.size()); // 新建列表,原列表留给异步任务处理 stagingData = new ArrayList<>(); }
方案3:等待异步任务完成后再清空(不推荐)
通过Future.get()阻塞主线程,等待异步任务执行完成后再清空列表,但这样会失去异步调用的意义,退化为同步执行:
if (stagingData.size() == 10000) { Future<String> future = databaseUpdateChanges.addInputDataStagingV12("tbl_staging", fieldQuery, config.getInputFields(), batchId, stagingData); futures.add(future); // 等待异步任务完成 try { future.get(); } catch (InterruptedException | ExecutionException e) { // 异常处理逻辑 logger.error("异步任务执行失败", e); } logger.info("staging size: {}", stagingData.size()); stagingData.clear(); }
内容的提问来源于stack exchange,提问作者Yugesh
相关产品推荐
相关产品推荐

