写在前面
前面分析的明白了,那现在我们就来复现下上文中的错误
正文
废话不多说,直接上代码:
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public void webPushTest() throws InterruptedException {
AtomicBoolean atomicBoolean=new AtomicBoolean(true);
for(int i=0;;){
appPushMessageClientMappingMapper.saveTest();
Thread t1=new Thread(new Runnable() {
public void run() {
try{
int i = clientManager.deleteClientIds(new ArrayList<String>(){{add("773479997251391488_iot_websocket_778553066747727872");}});
log.info("int {}",i);
}catch (Exception e){
log.error("e",e);
atomicBoolean.getAndSet(false);
}
}
},i+"t1");
Thread t2=new Thread(new Runnable() {
public void run() {
try{
int i = clientManager.deleteClientIds(new ArrayList<String>(){{add("773479997251391488_iot_websocket_778553066747727872");}});
log.info("int {}",i);
}catch (Exception e){
log.error("e",e);
atomicBoolean.getAndSet(false);
}
}
},i+"t2");
Thread t3=new Thread(new Runnable() {
public void run() {
try{
int i = clientManager.deleteClientIds(new ArrayList<String>(){{add("773479997251391488_iot_websocket_778553066747727872");}});
log.info("int {}",i);
}catch (Exception e){
log.error("e",e);
atomicBoolean.getAndSet(false);
}
}
},i+"t3");
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
if(!atomicBoolean.get()){
break;
}
Thread.sleep(500L);
i++;
}
}代码之所以这么写,是因为根据前面分析,此类死锁,产生的几个前提:
Delete操作,针对的是唯一索引上的等值查询的删除;(范围下的删除,也会产生死锁,但是死锁的场景,跟本文分析的场景,有所不同)
至少有3个(或以上)的并发删除操作;
并发删除操作,有可能删除到同一条记录,并且保证删除的记录一定存在;
事务的隔离级别设置为Repeatable Read,同时未设置innodb_locks_unsafe_for_binlog参数(此参数默认为FALSE);(Read Committed隔离级别,由于不会加Gap锁,不会有next key,因此也不会产生死锁)
使用的是InnoDB存储引擎;(废话!MyISAM引擎根本就没有行锁)
实际测试结果表示在以上条件下运行,在运行一定的时间后肯定会出现上述问题,如下:
问题复现~