记一次业务多线程遇到的问题
业务需求
每个小时从不同的表统计不同的数据,来对数据进行校核,时间规定15天以内的。
业务问题
统计的种类很多,需要从不同的表过滤,case when条件数据,各个表里的数据大概每天几百万,后面需要统计15天的数据。如果传统的循环按照时间,一天一天的统计,非常的慢,这个时候我们的索引什么都做了,非关系数据库暂时不能上,所有只能单纯的sql查询。这个时候需要用到多线程。
业务解决
service层查询需要处理时间,以时间循环,多线程查询。等于一个多线程查询一次时间,最后插入结果就行了。大概
1 | for(date:dates){ |
导致问题
多线程执行mapper查询数据库,查询不了,直接结束方法,有经验的都知道报异常了,这个时间去看异常信息,但是异常信息也没有打印出来。
解决问题
关键点是,我是直接调用service层方法,来做业务处理,开启多线程,然后mapper调用方法做处理,这个逻辑也没问题,但是突然报关闭连接,并且异常信息也不打印出来,只能debug才能看到异常信息。
面向谷歌编程也查不出所以然,只是知道mybatis的连接被关闭的。于是,猜测这个可能和主线程的被关闭有关。于是,又去谷歌下mybatis原理,这边看到一句关键的东西!!!
每次查询mybatis都会创建sqlSession吗?
答案:在同一个事物下,数据库操作不会创建sqlSession,反之推出一个结果, 为什么同一事物下,进行的增删改,可以回滚,因为他们使用同一个sqlSession,sqlSession直接回滚操作就行了。那么这个对这个问题有什么操作呢?
这边猜测是主线程被结束,导致sqlSession被关闭,导致其他线程调用mapper出现异常,导致直接结束方法。于是,这边又猜测代码会最终调用一个方法关闭sqlSession。
于是我们在把主线程sleep2000秒,就是下面这样
1 | for(date:dates){ |
神奇的事情发生了,多线程不报异常了,代码直接跑完结果。
于是,解决办法就是让主线程等其他线程执行完毕,然后主线程才可以结束。这个时候我们不可能一直sleep啊,并不知道线程结束时间。
我们,可以用CountDownLatch的await方法,来拦截主线程。
具体代码
1 | //创建计数器,次数是时间数组长度 |