我不确定我是否理解这个问题,但似乎它会构成线程T1的逻辑错误,该线程T1仅处理以AA开头的记录以将整个文件标记为“已解析”?例如,如果您的应用程序在 T1 更新后崩溃,但 T2 仍在处理 BB 记录,会发生什么情况?有些BB记录很可能会丢失,对吗?
无论如何,问题的症结在于您有一个争用条件,其中两个线程更新同一对象。过时的对象异常仅表示您的一个线程输掉了比赛。更好的解决方案可以完全避免比赛。
(我在这里假设单个记录处理是幂等的,如果不是这种情况,我认为你有更大的问题,因为某些故障模式会导致记录的重新处理。如果记录处理必须进行一次且只进行一次,那么您会遇到一个更难的问题,对于该问题,消息队列可能是更好的解决方案。
我将利用java.util.concurrent的功能将记录分派给线程工作线程,并让线程与休眠块进行交互,直到所有记录都被处理完毕,此时该线程可以将文件标记为“已解析”。
例如
// do something like this during initialization, or use a Guava LoadingCache...
Map<RecordType, Executor> executors = new HashMap<>();
// note I'm assuming RecordType looks like an enum
executors.put(RecordType.AA_RECORD, Executors.newSingleThreadExecutor());
然后在处理文件时,按如下方式分派每条记录,建立与排队任务状态相对应的未来列表。假设成功处理记录返回布尔值“true”:
List<Future<Boolean>> tasks = new ArrayList<>();
for (Record record: file.getRecords()) {
Executor executorForRecord = executors.get(record.getRecordType());
tasks.add(executor.submit(new RecordProcessor(record)));
}
现在等待所有任务成功完成 - 有更优雅的方法来做到这一点,特别是番石榴。请注意,如果你的任务失败并出现异常,你还需要在这里处理执行异常,我在这里掩盖了这一点。
boolean allSuccess = true;
for (Future<Boolean> task: tasks) {
allSuccess = allSuccess && task.get();
if (!allSuccess) break;
}
// if all your tasks completed successfully, update the file record
if (allSuccess) {
file.setStatus("Parsed");
}