在子列表上调用size()时发生ConcurrentModificationException

时间:2018-06-25 09:07:45

标签: java performance hibernate exception

我正在尝试保存一堆SQL事务。我处于ESB路由的上下文中,从SQL源传输到SQL目标,并且不能保证SQL事务的顺序,因此可以在插入对象之前进行SQL更新。

由于体系结构的缘故,我将这些SQL事务保存为1000 x 1000(我使用的是messageQueue)。因此,其中一些可能会失败,因此我将其重新路由以重试或拒绝它们。为了提高效率,我愿意改进较旧的系统,如果1000失败,则将1减1,以实现二分法(如果保存失败,请拆分列表,然后重试) ,通过递归性。我还要跟踪对象的属性,这要感谢另一个列表(objectsNo)进行进一步的操作。

但是,当我第一次递归时,调用objectsList.size()时却得到了一个 ConcurrentModificationException 。我该如何避免呢?我也很开放,非常感谢任何能提供除二分法之外的其他方法来提高效率的解决方案(并以此绕过我的问题)。

  

禁止显示:java.util.ConcurrentModificationException:空           在java.util.ArrayList $ SubList.checkForComodification(ArrayList.java:1231)           在java.util.ArrayList $ SubList.size(ArrayList.java:1040)           在fr.company.project.esbname.mariadb.MariaDbDatabase.saveObjectWithDichotomie(MariaDbDatabase.java:398)           在fr.company.project.esbname.mariadb.MariaDbDatabase.saveObjectWithDichotomie(MariaDbDatabase.java:404)           在fr.company.project.esbname.mariadb.MariaDbDatabase.saveObject(MariaDbDatabase.java:350)           在sun.reflect.GeneratedMethodAccessor324.invoke(未知来源)           在sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)           在java.lang.reflect.Method.invoke(Method.java:498)           在org.apache.camel.component.bean.MethodInfo.invoke(MethodInfo.java:472)           在org.apache.camel.component.bean.MethodInfo $ 1.doProceed(MethodInfo.java:291)           在org.apache.camel.component.bean.MethodInfo $ 1.proceed(MethodInfo.java:264)           在org.apache.camel.component.bean.BeanProcessor.process(BeanProcessor.java:178)           在org.apache.camel.management.InstrumentationProcessor.process(InstrumentationProcessor.java:77)           在org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:541)           ...省略了22个共同的框架

我试图理解,但不应有任何错误。即使使用递归,它也保持单线程。我认为问题可能出在休眠状态(保存失败的某些请求可能会保留在缓存中,并修改了锁),但是问题出在大小,它在原始列表的子列表上。

    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;
        }
    }
}

2 个答案:

答案 0 :(得分:3)

如果您会阅读sublist的文档,则明确指出:

  

此列表支持返回的列表,因此返回列表中的非结构性更改会反映在此列表中,反之亦然。

那是导致您的Exception的原因(不需要多个线程即可发生)。因此,当您创建一个新列表时,请通过以下方式创建它:

List<Object> objectsFirstHalf = new ArrayList<>(objects.subList(0, objects.size()/2));

答案 1 :(得分:2)

两件事:

  1. ConcurrentModificationException并不意味着该列表已被另一个线程修改,而是某些东西试图以预期的状态访问该列表,但同时它已更改。

  2. 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));
    }
}