线程池操作数据库造成死锁
一次遇到一个很有意思的现象,当我狂点分页的时候,因为一个接口返回超时了,过了一会系统崩了,任何请求都不会进来??
我就想当好奇了,每个请求都是一个线程,怎么会影响到其他的任务呢?于是我就抱着好奇的心理开始了查bug的旅程
首先我先进入页面,打开f12,狂点分页,之后其他请求也阻塞了,根本不能访问。
发现一个分页接口,竟然带着别的接口,这个updateEntInvoice
接口是干嘛的(其实是前端主动会去获取数据状态的接口),所以每一次分页都会携带着这个请求。
但是,很奇怪,不应该影响其他功能呀,应该是你走你的阳关道,我走我的独木桥呀
再往下看 updateEntInvoice
接口
rivate List<GiftInvoiceResp> updateGiftInvoice(List<String> updateInvoiceIdList) {
// list 分批执行
List<GiftInvoiceResp> entInvoiceResponseList = new ArrayList<>();
try {
int ai = atomicInteger.get();// 初始值8
if (ai <= 0) {
log.error("请求数过多,更新开票任务丢弃:{}", ai);
return entInvoiceResponseList;
} else {
atomicInteger.getAndDecrement();
}
// 创建线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 3, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadPoolExecutor.CallerRunsPolicy());
for(String orderBillId : updateInvoiceIdList) {
threadPoolExecutor.submit(() -> {
数据库操作1;
数据库操作2;
数据库操作3;
})
}
.....
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
大概思路:
1.每次有请求进来,会有一个计数器atomicInteger
进来一个请求就减1,直到为0之后,就放弃后面的请求。
2. 每次请求会去创建线程池,创建task集合,每个task里面是要执行的数据库操作(更新操作)。
看到这里是不是都会想,是不是线程互相抢占资源,死锁了??
带着这个疑问我自己写了个demo(不敢改,万一出错了咋办)
照葫芦画瓢,写个简单版本的,请求的时候,果然也是这样
每次方法进来新建一个线程池,操作数据库。
后面,同事说是不是连接数不够了,我觉得有这个可能。
把druid的最大并发连接数改成了11,神奇的一幕出现了,每个线程顺利完成操作
首先我们可以看到druid的最大并发连接数默认是max_active=8
而我测试的时候是有10个线程,等于就是说需要有十个连接来连接数据库,最大并发连接数是8个,才会出现 java.lang.Thread.State: WAITING (parking)
情况
# but!!!
线程wait的原因还是不知道,之后再来更吧......
"pool-8-thread-2" #87 prio=5 os_prio=0 tid=0x0000016497875000 nid=0x6120 waiting on condition [0x0000003f269fd000]
java.lang.Thread.State: WAITING (parking)
at jdk.internal.misc.Unsafe.park(java.base@9.0.4/Native Method)
- parking to wait for <0x00000006e276f730> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(java.base@9.0.4/LockSupport.java:194)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(java.base@9.0.4/AbstractQueuedSynchronizer.java:2062)
at com.alibaba.druid.pool.DruidDataSource.takeLast(DruidDataSource.java:2185)
at com.alibaba.druid.pool.DruidDataSource.getConnectionInternal(DruidDataSource.java:1678)
at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:1415)
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1395)
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1385)
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:100)
at org.springframework.jdbc.datasource.DataSourceUtils.fetchConnection(DataSourceUtils.java:158)
at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:116)
at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:79)
at org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction.java:82)
at org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction.java:68)
at org.apache.ibatis.executor.BaseExecutor.getConnection(BaseExecutor.java:336)
at com.baomidou.mybatisplus.core.executor.MybatisSimpleExecutor.prepareStatement(MybatisSimpleExecutor.java:93)
at com.baomidou.mybatisplus.core.executor.MybatisSimpleExecutor.doQuery(MybatisSimpleExecutor.java:66)
at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:324)
at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:156)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:109)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:83)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:147)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:76)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(java.base@9.0.4/Native Method)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(java.base@9.0.4/NativeMethodAccessorImpl.java:62)
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(java.base@9.0.4/DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(java.base@9.0.4/Method.java:564)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:433)
at com.sun.proxy.$Proxy60.selectOne(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:166)
at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute(MybatisMapperMethod.java:89)
at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:62)
at com.sun.proxy.$Proxy61.selectById(Unknown Source)
at com.baomidou.mybatisplus.extension.service.impl.ServiceImpl.getById(ServiceImpl.java:238)
at com.zhu.multiple_thread.service.impl.UserServiceImpl.lambda$multipleThread$0(UserServiceImpl.java:41)
at com.zhu.multiple_thread.service.impl.UserServiceImpl$$Lambda$813/1635005837.call(Unknown Source)
at java.util.concurrent.FutureTask.run$$$capture(java.base@9.0.4/FutureTask.java:264)
at java.util.concurrent.FutureTask.run(java.base@9.0.4/FutureTask.java)
at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@9.0.4/ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@9.0.4/ThreadPoolExecutor.java:641)
at java.lang.Thread.run(java.base@9.0.4/Thread.java:844)
Locked ownable synchronizers:
- <0x00000006e8b01530> (a java.util.concurrent.ThreadPoolExecutor$Worker)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
上次更新: 2022/04/26, 20:34:33