业务需求

每个小时从不同的表统计不同的数据,来对数据进行校核,时间规定15天以内的。

业务问题

统计的种类很多,需要从不同的表过滤,case when条件数据,各个表里的数据大概每天几百万,后面需要统计15天的数据。如果传统的循环按照时间,一天一天的统计,非常的慢,这个时候我们的索引什么都做了,非关系数据库暂时不能上,所有只能单纯的sql查询。这个时候需要用到多线程。

业务解决

service层查询需要处理时间,以时间循环,多线程查询。等于一个多线程查询一次时间,最后插入结果就行了。大概

1
2
3
4
5
for(date:dates){
new Thread(()->{
业务逻辑
}).start
}

导致问题

多线程执行mapper查询数据库,查询不了,直接结束方法,有经验的都知道报异常了,这个时间去看异常信息,但是异常信息也没有打印出来。

解决问题

关键点是,我是直接调用service层方法,来做业务处理,开启多线程,然后mapper调用方法做处理,这个逻辑也没问题,但是突然报关闭连接,并且异常信息也不打印出来,只能debug才能看到异常信息。

面向谷歌编程也查不出所以然,只是知道mybatis的连接被关闭的。于是,猜测这个可能和主线程的被关闭有关。于是,又去谷歌下mybatis原理,这边看到一句关键的东西!!!

每次查询mybatis都会创建sqlSession吗?

答案:在同一个事物下,数据库操作不会创建sqlSession,反之推出一个结果, 为什么同一事物下,进行的增删改,可以回滚,因为他们使用同一个sqlSession,sqlSession直接回滚操作就行了。那么这个对这个问题有什么操作呢?

这边猜测是主线程被结束,导致sqlSession被关闭,导致其他线程调用mapper出现异常,导致直接结束方法。于是,这边又猜测代码会最终调用一个方法关闭sqlSession。

于是我们在把主线程sleep2000秒,就是下面这样

1
2
3
4
5
6
for(date:dates){
new Thread(()->{
业务逻辑
}).start
}
Thread.sleep(2000000);

神奇的事情发生了,多线程不报异常了,代码直接跑完结果。

于是,解决办法就是让主线程等其他线程执行完毕,然后主线程才可以结束。这个时候我们不可能一直sleep啊,并不知道线程结束时间。

我们,可以用CountDownLatch的await方法,来拦截主线程。

具体代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//创建计数器,次数是时间数组长度
CountDownLatch call = new CountDownLatch(dates.size);
for(date:dates){
new Thread(()->{
try(){
业务逻辑
//任务完毕,计数器-1
call.countDown();
}catch(Exception e){
//任务完毕,计数器-1,异常同样要-1,否则就卡住了
call.countDown();
}

}).start
//拦截方法,直到计数器为0
call.await();
}