Spring boot 为什么mybatis在这个并发场景中报告这个错误,springboot集成的sqlsessiontemplate不是线程安全的?

Spring boot 为什么mybatis在这个并发场景中报告这个错误,springboot集成的sqlsessiontemplate不是线程安全的?,spring-boot,thread-safety,mybatis,Spring Boot,Thread Safety,Mybatis,**菲斯特。我的项目是关于推荐文章的列表,每一篇文章都有自己的规则,所以我使用AsyncTaskExecutor这个工具类来并发查询不同的文章,现在,一些规则是特殊的,所以我将它们分成两部分。下面是我的代码: 我用springboot+mybatis来做** @Bean public AsyncTaskExecutor dataTaskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

**菲斯特。我的项目是关于推荐文章的列表,每一篇文章都有自己的规则,所以我使用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);
    }
上面标记了报告错误的位置。我不明白为什么mybatis应该是线程安全的。

官方文档称它是线程安全的。


官方文档称它是线程安全的。

SqlSessionTemplate
本身是线程安全的。问题出在您的代码中

异常显示错误发生在
foreach
元素中。请注意stacktrace的这一部分:

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。此集合由另一个线程同时修改。集合上的迭代器已内置检查集合是否未修改,此检查表明存在问题

为了修复这个问题,需要同步访问多个线程中使用的集合,这样当使用集合来查询基于它的一些数据时,这会发生原子化,并且在查询生成中间不会发生修改。

一个可能的原因是未分析此
wait
的结果:

latch1.await(10, TimeUnit.SECONDS);
如果处理时间超过10秒,则第二部分开始执行,而查询所基于的数据仍在修改中。这可能会发生,因为要做的工作量取决于数据


您需要检查此
的结果等待
,并且在过程第一部分中的所有任务完成之前不要继续处理。

SqlSessionTemplate本身是线程安全的。问题出在您的代码中

异常显示错误发生在
foreach
元素中。请注意stacktrace的这一部分:

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。此集合由另一个线程同时修改。集合上的迭代器已内置检查集合是否未修改,此检查表明存在问题

为了修复这个问题,需要同步访问多个线程中使用的集合,这样当使用集合来查询基于它的一些数据时,这会发生原子化,并且在查询生成中间不会发生修改。

一个可能的原因是未分析此
wait
的结果:

latch1.await(10, TimeUnit.SECONDS);
如果处理时间超过10秒,则第二部分开始执行,而查询所基于的数据仍在修改中。这可能会发生,因为要做的工作量取决于数据


您需要检查此
的结果,等待
,在程序第一部分中的所有任务完成之前不要继续处理。

首先,非常感谢您的回答,但我还有一个问题。开始时,在代码的第一部分中,我没有使用多线程表单进行查询,而是使用单线程循环查询。此时,多线程查询的第二部分没有问题,但是当我将第一部分转换为多线程查询时,第二部分开始报告错误。而且 尝试{latch1.await(10,TimeUnit.SECONDS)}catch(InterruptedException e){log.error('多线等待异常:", e) }这部分代码应该与两个并发代码分开,我不明白为什么修改第一部分的逻辑,这将影响第二部分的动态sql形成。非常感谢您的回答。开始时,当我将等待时间设置为2秒时,我报告了此错误。我只是没想到会超过10秒。但从理论上讲,这是唯一可能的。非常感谢您的解决方案首先,我非常感谢您的回答,但我还有一个问题。开始时,在代码的第一部分,我没有使用多线程形式进行查询,而是使用单线程循环查询。此时,多线程的第二部分取消查询没有问题,但是当我将第一部分转换为多线程查询时,第二部分开始报告错误。而且 尝试{latch1.await(10,TimeUnit.SECONDS)}catch(InterruptedException e){log.error('多线等待异常:“,e);}这部分代码应该与两个并发代码分开,我不明白为什么修改第一部分的逻辑,这将影响第二部分的动态sql形成。非常感谢您的回答。开始时,当我将等待时间设置为2秒时,我报告了此错误。我只是没想到会超过10秒。但从理论上讲,只有这个原因才有可能。非常感谢您的解决方案