关于Spring框架下的mysql事务用多线程测试,实际与构想有差距

时间:2021-12-06 20:56:46
构想:事务a开始,更新了所有数据,读取显示。然后事务b开始,更新一个数据,插入一个数据,事务b结束。事务a继续读取所有数据,事务a结束。
实际:事务a开始,更新了所有数据,读取显示。然后事务b刚开始,就开始等待事务a完成,然后才更新一个数据,插入一个数据,事务b结束。
用的是mysql数据库,默认是Repeatable_Read 隔离级别
实现代码:

@Service
public class GradeService {

@Autowired
GradeDao dao;
public List<Grade> find() {
return dao.find();
}

//事务B,删除一个学生后,增加一个学生
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void testTran() {
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("事务B操作开始");
//开始修改、插入
System.out.println("a");
dao.update(new Grade(15,"java"));
System.out.println("b");
dao.add(new Grade(9,"六年级"));
System.out.println("事务B操作结束");

}
//事务A
@Transactional(isolation=Isolation.REPEATABLE_READ)
public void testA(){
System.out.println("A事务可重复读开始");
System.out.println("读取全部数据");
System.out.println("范围操作开始");
dao.update("五年级");
List<Grade> list = dao.find();
for (Grade grade : list) {
System.out.println(grade);
}
System.out.println("----");
//读取修改后的数据以及插入后的数据
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----");
//读取修改前的数据以及插入前的数据
System.out.println("update数据 "+dao.findById(15));
/* System.out.println("add数据"+dao.findById(9));*/
System.out.println("全部数据");
List<Grade> list1 = dao.find();
for (Grade grade : list1) {
System.out.println(grade);
}
System.out.println("A事务可重复读结束");
}
}


使用除了主线程,还有另外一个线程

public static void main(String[] args) {
ClassPathXmlApplicationContext xml = Factory.getXml();
GradeController bean = xml.getBean(GradeController.class);
Runner runner = new Runner();
new Thread(runner).start();//最终调用testA()
bean.test1();//最终调用testTran()
try {
Thread.sleep(30000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Runner implements Runnable{
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ClassPathXmlApplicationContext xml = Factory.getXml();
GradeController bean = xml.getBean(GradeController.class);
bean.test2();
}
}

测试结果:

A事务可重复读开始
读取全部数据
范围操作开始
Grade [gradeId=1, gradeName=五年级]
Grade [gradeId=2, gradeName=五年级]
Grade [gradeId=3, gradeName=五年级]
Grade [gradeId=4, gradeName=五年级]
Grade [gradeId=5, gradeName=五年级]
Grade [gradeId=6, gradeName=五年级]
Grade [gradeId=7, gradeName=五年级]
Grade [gradeId=8, gradeName=五年级]
Grade [gradeId=15, gradeName=五年级]
----
事务B操作开始
a
----
update数据 Grade [gradeId=15, gradeName=五年级]
全部数据
Grade [gradeId=1, gradeName=五年级]
Grade [gradeId=2, gradeName=五年级]
Grade [gradeId=3, gradeName=五年级]
Grade [gradeId=4, gradeName=五年级]
Grade [gradeId=5, gradeName=五年级]
Grade [gradeId=6, gradeName=五年级]
Grade [gradeId=7, gradeName=五年级]
Grade [gradeId=8, gradeName=五年级]
Grade [gradeId=15, gradeName=五年级]
A事务可重复读结束
b
事务B操作结束

引用
实在搞不懂为什么事务B会等事务A结束才执行?两个线程不该互不干扰吗?求教各位前辈指点,谢谢

3 个解决方案

#1


更新同一条记录有行锁,自然得等你A事务提交之后B事务才会拿到锁去做更新操作

#2


懂一些了,请问索引锁是什么?

#3


引用 1 楼 maradona1984 的回复:
更新同一条记录有行锁,自然得等你A事务提交之后B事务才会拿到锁去做更新操作


懂一些了,请问索引锁是什么? 

#1


更新同一条记录有行锁,自然得等你A事务提交之后B事务才会拿到锁去做更新操作

#2


懂一些了,请问索引锁是什么?

#3


引用 1 楼 maradona1984 的回复:
更新同一条记录有行锁,自然得等你A事务提交之后B事务才会拿到锁去做更新操作


懂一些了,请问索引锁是什么?