Я пытаюсь сохранить кучу транзакций SQL. Я нахожусь в контексте маршрутов ESB при передаче из источника SQL в цель SQL, и порядок транзакций SQL не гарантируется, поэтому вы можете выполнить обновление SQL до того, как объект был вставлен.
Благодаря архитектуре я сохраняю эти SQL-транзакции 1000 на 1000 (я использую messageQueue). Так что некоторые из них могут выйти из строя, и я перенаправляю их, чтобы повторить попытку или отклонить их. Чтобы повысить эффективность, я готов улучшить старую систему, где в случае сбоя 1000 вы сохраняете 1 к 1, чтобы реализовать дихотомия (если сохранение не удалось, вы разделите список и повторите попытку) через рекурсивность. Я также отслеживаю атрибут своих объектов благодаря другому списку (objectsNo) для дальнейших операций.
Однако я получаю ConcurrentModificationException при первой рекурсивности при вызове objectsList.size (). Как мне этого избежать? Я также открыт и был бы очень благодарен любым решениям, которые предоставили бы другой способ, кроме дихотомии, улучшить эффективность (и таким образом обошли бы мою проблему).
Suppressed: java.util.ConcurrentModificationException: null at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1231) at java.util.ArrayList$SubList.size(ArrayList.java:1040) at fr.company.project.esbname.mariadb.MariaDbDatabase.saveObjectWithDichotomie(MariaDbDatabase.java:398) at fr.company.project.esbname.mariadb.MariaDbDatabase.saveObjectWithDichotomie(MariaDbDatabase.java:404) at fr.company.project.esbname.mariadb.MariaDbDatabase.saveObject(MariaDbDatabase.java:350) at sun.reflect.GeneratedMethodAccessor324.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.apache.camel.component.bean.MethodInfo.invoke(MethodInfo.java:472) at org.apache.camel.component.bean.MethodInfo$1.doProceed(MethodInfo.java:291) at org.apache.camel.component.bean.MethodInfo$1.proceed(MethodInfo.java:264) at org.apache.camel.component.bean.BeanProcessor.process(BeanProcessor.java:178) at org.apache.camel.management.InstrumentationProcessor.process(InstrumentationProcessor.java:77) at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:541) ... 22 common frames omitted
Я пытался понять, но ошибки быть не должно. Даже если бы я использовал рекурсивность, он остался однопоточным. Я считал, что проблема может быть в спящем режиме (некоторые запросы из сбойного сохранения могут остаться в кеше и заблокировать модификацию), но проблема связана с размером, который находится в подсписке исходного списка.
private List<String> saveObjectWithDichotomie(List<Object> objects,
List<String> objectsNo,
Exchange exchange) throws JsonProcessingException {
try {
objectRepository.save(objects);
return objectsNo;
} catch (DataIntegrityViolationException e) {
if (objects.size() == 1) {
objectsNo.clear();
errorProcessor.sendErrorToRejets(objects.get(0), exchange, e);
return objectsNo;
} else {
List<Object> objectsFirstHalf = objects.subList(0, objects.size()/2);
List<Object> objectsSecondHalf = objects.subList(objects.size()/2, objects.size());
List<String> objectsNoFirstHalf = objectsNo.subList(0, objectsNo.size()/2);
List<String> objectsNoSecondHalf = objectsNo.subList(objectsNo.size()/2, objectsNo.size());
objectsNo.clear();
objectsNo.addAll(
saveObjectWithDichotomie(objects, objectsNoFirstHalf, exchange)
);
objectsNo.addAll(
saveObjectWithDichotomie(objects, objectsNoSecondHalf, exchange)
);
return objectsNo;
}
}
}
Возможно, вы правы, но я не понимаю почему, я просто называю размер в списке, а затем делаю из него подсписок. Я ничего не удаляю и не добавляю. Я искал подобные проблемы раньше и видел ответы, подобные вашему, но я не думаю, что это имеет отношение к моей проблеме.
похоже, что это связано с тем фактом, что вы используете sublist, который документально подтвержден этим списком, поэтому изменения в одном отражаются в другом и наоборот, я считать вам следует создать новый List, например List<Object> objectsFirstHalf = new ArrayList<>(objects.subList(0, objects.size()/2));
Большое спасибо за ответ ! Я не знал, что этот подсписок был ссылкой на исходный список. Я попробую ваше решение. Если это сработает, подумайте об ответе на вопрос, чтобы я мог принять ваш ответ.
отправил ответ ... и милости просим




Две вещи:
ConcurrentModificationException не означает, что список был изменен другим потоком, но что что-то пытается получить доступ к списку в ожидаемом состоянии, но тем временем он был изменен.
subList не создает фактический новый список, он создает представление в исходном списке. Это означает, что вы не можете изменить исходный список, не сделав полученный подсписок недействительным.
Так,
objectsNo.clear();
это твоя проблема.
Смотрите этот MCVE:
public class Sublist {
public static void main(String[] args) {
List<String> list = new ArrayList<>(
IntStream.range(0, 100).mapToObj(Integer::toString).collect(Collectors.toList()));
List<String> sublist = list.subList(10, 20);
// outputs "15"
System.out.println(sublist.get(5));
list.clear();
// throws ConcurrentModificationException
System.out.println(sublist.get(5));
}
}
Спасибо daniu за ваш ответ, особенно за очень полезный пример. Однако я принял ответ Евгения, потому что он первым ответил мне в комментариях.
Если вы прочитали документацию sublist, ясно сказано:
The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa.
Это является причиной вашего исключения (для этого нет необходимости в нескольких потоках). Таким образом, когда вы создаете новый список, создайте его с помощью:
List<Object> objectsFirstHalf = new ArrayList<>(objects.subList(0, objects.size()/2));
вы изменяете свой список при удалении или добавлении чего-либо в него, поэтому вы получаете исключение