Почему mybatis сообщает об этой ошибке в этом параллельном сценарии, интегрированный шаблон Springboot sqlsessiontemplate не является поточно-ориентированным? - PullRequest
0 голосов
/ 23 октября 2019

** фисрт. Мой проект посвящен списку рекомендуемых статей, у каждой статьи есть свое собственное правило, поэтому я использую этот инструментальный класс AsyncTaskExecutor для одновременного запроса разных статей, и теперь некоторые правила особенные, поэтому я разделил их на разные правила на две части. следующий мой код: я использую springboot + mybatis, чтобы сделать **

@Bean
public AsyncTaskExecutor dataTaskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(16);
    executor.setThreadNamePrefix("data_task_executor-");
    return executor;
}

здесь я инициализация AsyncTaskExecutor класс для готовности Далее приведен частичный код параллельного запроса.

    // here i get different rule list

    List<Rule> ruleList = JSON.parseArray(scene.getRules(), Rule.class);
    Iterator<Rule> ruleIterator = ruleList.iterator();
    CountDownLatch latch1 = new CountDownLatch(ruleList.size());
    while (ruleIterator.hasNext()) {
       Rule ruleNext = ruleIterator.next();
       // unAsyncScenes is a array,this rule query in here
       if (Arrays.binarySearch(unAsyncScenes, ruleNext.getSource()) >= 0) { 
          dataTaskExecutor.execute(() -> {
              try {
                  searchIDSByRule(idWithRtsMap, articleReferralList, sceneId, feedSum, userId, isNewUserByHistory, discussHistoryList, discussList, graphHistorys, ruleNext);
                   //Record browsing history
                   graphHistorys.addAll(idWithRtsMap.keySet());
              } catch (Exception e) {
                  log.warn("子规则查图失败", e);
              } finally {
                  latch1.countDown();
              }

          });
          //Query deleted
          ruleIterator.remove();
      } else {
          latch1.countDown();
      }

    }
    try {
        latch1.await(10, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
        log.error("多线等待异常:", e);
    }
    //deal with Duplicate article
    Set<Long> articleSet = new HashSet();
    articleReferralList.forEach(article -> articleSet.add(article));
    if (articleReferralList.size() != articleSet.size()) {
        log.warn("出现了重复的文章");
        articleReferralList.clear();
        articleReferralList.addAll(articleSet);
    }       
    final CountDownLatch latch = new CountDownLatch(ruleList.size());   
    for (Rule rule : ruleList) {
        // second concurrent query(query for other article)
        dataTaskExecutor.execute(() -> {
            try { *****// here hava error!!!!!!!!!!!!***** 
                searchIDSByRule(idWithRtsMap, articleReferralList, sceneId, feedSum, userId, isNewUserByHistory, discussHistoryList, discussList, graphHistorys, rule);
            } catch (Exception e) {
                log.warn("子规则查图失败", e);
            } finally {
                latch.countDown();
            }
        });
    }
    try {
        latch.await(10, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
         log.error("多线等待异常:", e);
    }

это все код запроса, но я запустил этот код, иногда он выдает ошибку, подобную этой:

org.apache.ibatis.exceptions.PersistenceException: \
### Error querying database. Cause: java.util.ConcurrentModificationException\
### Cause: java.util.ConcurrentModificationException\
    at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:77) ~[mybatis-spring-1.3.1.jar!\/:1.3.1]\
    at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446) ~[mybatis-spring-1.3.1.jar!\/:1.3.1]\
    at com.sun.proxy.$Proxy91.selectList(Unknown Source) ~[?:?]\
    at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:230) ~[mybatis-spring-1.3.1.jar!\/:1.3.1]\
    at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:137) ~[mybatis-3.4.5.jar!\/:3.4.5]\
    at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:75) ~[mybatis-3.4.5.jar!\/:3.4.5]\
    at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59) ~[mybatis-3.4.5.jar!\/:3.4.5]\
    at com.sun.proxy.$Proxy131.searchBySigAndExample(Unknown Source) ~[?:?]\
    at com.coffee.ref.service.impl.ReferralServiceImpl.searchIDSByRule(ReferralServiceImpl.java:842) ~[classes!\/:0.0.1]\
    at com.coffee.ref.service.impl.ReferralServiceImpl.lambda$findArticleIDSByRule$7(ReferralServiceImpl.java:625) ~[classes!\/:0.0.1]\
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_212]\
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_212]\
    at java.lang.Thread.run(Thread.java:748) [?:1.8.0_212]\
Caused by: org.apache.ibatis.exceptions.PersistenceException: \
### Error querying database. Cause: java.util.ConcurrentModificationException\
### Cause: java.util.ConcurrentModificationException\
    at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30) ~[mybatis-3.4.5.jar!\/:3.4.5]\
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:150) ~[mybatis-3.4.5.jar!\/:3.4.5]\
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:141) ~[mybatis-3.4.5.jar!\/:3.4.5]\
    at sun.reflect.GeneratedMethodAccessor143.invoke(Unknown Source) ~[?:?]\
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_212]\
    at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_212]\
    at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:433) ~[mybatis-spring-1.3.1.jar!\/:1.3.1]\
    ... 11 more\
Caused by: java.util.ConcurrentModificationException\
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909) ~[?:1.8.0_212]\
    at java.util.ArrayList$Itr.next(ArrayList.java:859) ~[?:1.8.0_212]\
    at org.apache.ibatis.scripting.xmltags.ForEachSqlNode.apply(ForEachSqlNode.java:62) ~[mybatis-3.4.5.jar!\/:3.4.5]\
    at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:33) ~[mybatis-3.4.5.jar!\/:3.4.5]\
    at org.apache.ibatis.scripting.xmltags.IfSqlNode.apply(IfSqlNode.java:35) ~[mybatis-3.4.5.jar!\/:3.4.5]\
    at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:33) ~[mybatis-3.4.5.jar!\/:3.4.5]\
    at org.apache.ibatis.scripting.xmltags.DynamicSqlSource.getBoundSql(DynamicSqlSource.java:41) ~[mybatis-3.4.5.jar!\/:3.4.5]\
    at org.apache.ibatis.mapping.MappedStatement.getBoundSql(MappedStatement.java:292) ~[mybatis-3.4.5.jar!\/:3.4.5]\
    at com.github.pagehelper.PageInterceptor.intercept(PageInterceptor.java:83) ~[pagehelper-5.1.2.jar!\/:?]\
    at org.apache.ibatis.plugin.Plugin.invoke(Plugin.java:61) ~[mybatis-3.4.5.jar!\/:3.4.5]\
    at com.sun.proxy.$Proxy188.query(Unknown Source) ~[?:?]\
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:148) ~[mybatis-3.4.5.jar!\/:3.4.5]\
    at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:141) ~[mybatis-3.4.5.jar!\/:3.4.5]\
    at sun.reflect.GeneratedMethodAccessor143.invoke(Unknown Source) ~[?:?]\
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_212]\
    at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_212]\
    at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:433) ~[mybatis-spring-1.3.1.jar!\/:1.3.1]\
    ... 11 more\"}"]

Место, где было сообщено об ошибке, отмечено выше. Я непонять, почему mybatis должен быть безопасным для потоков.

Ответы [ 2 ]

0 голосов
/ 23 октября 2019

SqlSessionTemplate сам по себе потокобезопасен. Проблема в вашем коде.

Исключение показывает, что ошибка возникает в элементе foreach. Обратите внимание на этот фрагмент трассировки стека:

Caused by: java.util.ConcurrentModificationException\
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909) ~[?:1.8.0_212]\
    at java.util.ArrayList$Itr.next(ArrayList.java:859) ~[?:1.8.0_212]\
    at org.apache.ibatis.scripting.xmltags.ForEachSqlNode.apply(ForEachSqlNode.java:62) ~[mybatis-3.4.5.jar!\/:3.4.5]\

Так что же здесь происходит? В маппере вы строите SQL динамически, перебирая некоторые коллекции. Эта коллекция одновременно изменяется другим потоком. Итератор коллекции имеет встроенную проверку того, что коллекция не изменена, и эта проверка указывает на наличие проблемы.

Чтобы это исправить, вам нужно синхронизировать доступ к коллекции, которая используется в нескольких потоках, чтобы при использовании коллекции для запроса некоторых данных на ее основе это происходило атомарно, а в середине никакие изменения не происходили. генерации запроса.

Одной из возможных причин этого является то, что результат этого await не анализируется:

latch1.await(10, TimeUnit.SECONDS);

Если обработка занимает более 10 секунд, начинается вторая частьвыполнение, пока данные, на которых основан запрос, все еще изменяются. Это может произойти, так как объем работы зависит от данных.

Вам необходимо проверить результат этого await и не продолжать обработку, пока все задачи в первой части процедуры не будут завершены.

0 голосов
/ 23 октября 2019

введите описание изображения здесь

Официальная документация гласит, что это потокобезопасно.

...