• 150632

    文章

  • 1244

    评论

  • 13

    友链

  • 最近新加了换肤功能,大家多来逛逛吧~~~~
  • 喜欢这个网站的朋友可以加一下QQ群,我们一起交流技术。

多线程操作数据库,保证事务的唯一性(真实有效)


网上搜罗了一大堆所谓的多线程操作数据库,保证事务同时提交回滚啊,有的文章有点含量,有的就是纯属胡诌,浪费同学们的感情和时间,不过他们同时给了我一大堆经验和教训,从而让我实现了我认为有效可行的方案。废话不多说,直接上代码

1.首先创建一个自有的多线程执行器,当然你用spring已有的也没毛病

@Component
public class MyExecutor {

    private static int maximumPoolSize = Runtime.getRuntime().availableProcessors();

    @Bean("/sqlTask")
    public ExecutorService getThreadPool() {
        ThreadFactory factory = new ThreadFactoryBuilder().setNameFormat("myThread-handle-%d").get();

        int corePoolSize = 2;
        return new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(500),factory);
    }
}

2.创建一个获取sqlsession的类,当然直接在你的方法类中实现也可以

@Component
public class SqlSessionContext {

    @Autowired
    private SqlSessionTemplate sqlSessionTemplate;

    public SqlSession getSqlSession(){
        SqlSessionFactory sqlSessionFactory = sqlSessionTemplate.getSqlSessionFactory();
        return sqlSessionFactory.openSession();
    }
}

3.service层怎么注入我就不写了,这都不会的话直接就不用看了吧

    public Boolean batchAdd() throws Exception {
        boolean success = true;
        
        // 获取执行器
        ExecutorService service = myExecutor.getThreadPool();

        // 构建对象信息
        User user1 = buildUser(1);
        User user2 = buildUser(2);
        
        List<Callable<Integer>> callable = new ArrayList<>();

        // 获取数据库连接(内部创建了自有事务)
        SqlSession sqlSession = sqlSessionContext.getSqlSession();
        Connection connection = sqlSession.getConnection();
        // 设置手动提交
        connection.setAutoCommit(false);
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        try {
            // 多线程调用
            callable.add(()->userService1.call(user1,userMapper));
            callable.add(()->userService2.call(user2,userMapper));
            List<Future<Integer>> futures = service.invokeAll(callable);

            // 统计执行结果
            int num = 0;
            for (Future<Integer> future : futures) {
                num += future.get();
            }
            // 所有线程执行成功则=2,直接提交,否则回滚
            if (num == 2){
                connection.commit();
            }else {
                success = false;
                connection.rollback();
            }
        }catch (Exception e){
            e.printStackTrace();
            connection.rollback();
            success = false;
        }finally {
            connection.close();
        }
        return success;
    }

4.userService1的实现,userService2和它类似

@Component
public class UserService1 {

    Integer call(User user, UserMapper userMapper){

        try {
            userMapper.insert(user);
            Integer.parseInt("aa"); // 手动制造异常,看是否不插入数据
            return 1;
        }catch (Exception e){
            System.out.println(Thread.currentThread().getName()+"failure");
        }
        return 0;
    }
}

看着挺简单,但是当时设计的时候费了很大的劲,用声明式事务啊,多线程创建自有事务啊,整了老半天也没成功。通过看日志,发现在事务真正提交的时候,sqlsession已经释放了,并且线程内的事务没有全部绑定到主事务中,我就想为何我不直接使用sqlsession去管理呢,毕竟最终的插入操作时mybatis控制的,看源码发现sqlsession是控制mybatis事务的管家,而且mybatis也提供了便于获取session的SqlSessionTemplate,趁着热血上头搞一波,最终还真好使。代码看上去没啥问题,并发测试也没问题,但是没有经过大数据量,复杂操作的验证,所有只给各位同学一点灵感,欢迎踩坑。

最后附上源代码地址:多线程操作数据库源码


695856371Web网页设计师②群 | 喜欢本站的朋友可以收藏本站,或者加入我们大家一起来交流技术!

1条评论

Loading...
  • qxy1423L

    这样开启事务后,所有任务都会在线程池中的某一个线程中串行执行,不会在多个线程中并发执行



发表评论

电子邮件地址不会被公开。 必填项已用*标注

自定义皮肤 主体内容背景
打开支付宝扫码付款购买视频教程
遇到问题联系客服QQ:419400980
注册梁钟霖个人博客