首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为什么在这个并发场景中报告这个错误,springboot集成sqlsessiontemplate不是线程安全的呢?

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

Stack Overflow用户
提问于 2019-10-23 02:51:52
回答 2查看 537关注 0票数 1

**财政。我的项目是关于推荐的文章列表,每一篇文章都有自己的规则,所以我使用AsyncTaskExecutor这个工具类来并发查询不同的文章,而现在,有些规则是特殊的,所以我把它们分成两部分。下面是我的代码:我使用springboot + mybatis来做**

代码语言:javascript
运行
复制
@Bean
public AsyncTaskExecutor dataTaskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(16);
    executor.setThreadNamePrefix("data_task_executor-");
    return executor;
}

这里我初始化了AsyncTaskExecutor类,接下来是并发查询的部分代码.

代码语言:javascript
运行
复制
    // 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);
    }

这都是查询代码,但我运行了这段代码,有时会出现如下错误:

代码语言:javascript
运行
复制
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应该是线程安全的。

EN

回答 2

Stack Overflow用户

发布于 2019-10-23 02:54:07

在这里输入图像描述

官方文件显示它是线程安全的。

票数 0
EN

Stack Overflow用户

发布于 2019-10-23 08:52:51

SqlSessionTemplate本身是线程安全的。问题在于你的代码。

异常显示错误发生在foreach元素中。请注意堆栈跟踪的以下部分:

代码语言:javascript
运行
复制
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的结果:

代码语言:javascript
运行
复制
latch1.await(10, TimeUnit.SECONDS);

如果处理时间超过10秒,则第二部分开始执行,而查询所基于的数据仍在修改。这可能是因为要做的工作量取决于数据。

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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58514781

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档