前言:
我们在开发过程中,为了解决高并发的问题,通常会选择加锁,以此来让程序排队执行,这样避免出现数据查询后判断的错乱,导致判断失效,数据重复,重复执行某些程序的目的。
那么,你可曾想过有一天你的锁会失效?
我们先来看一下啊以下的代码:
代码语言:javascript复制synchronized (Api.class) {
resultVo = service.relationDept(data);
}
代码语言:javascript复制@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public ResultVo<String> relationDept(String data) {
//校验是否已经绑定此部门
int row = dataSubscribeDao.selectCountRDepartmentWorker(deptFkCode, workerFkCode);
if (row == 0) {
RDepartmentWorker rDepartmentWorker = new RDepartmentWorker();
rDepartmentWorker.setDepartmentFkCode(deptFkCode);
// …… 省略其它赋值代码
dao.insertRDepartmentWorker(rDepartmentWorker);
//加入正常部门后,要删除加入的本校区默认部门
Long defaultDepFkCode = dao.getDefaultDepBySchool(schoolFkCode);
if (defaultDepFkCode != null) {
dao.deleteWorkerDefaultDepartment(workerFkCode, defaultDepFkCode);
}
}
如上代码,在绑定部门的时候,要先查询用户是否已经绑定了部门,从而做出判断,没有判定就判定,同时删除已绑定的默认部门。为了防止并发问题,所以在把整个方式加上同步锁。
实际上,当你运行这段代码以后,会有一定的概率出现判断失效,给人“锁失效”的感觉,其实并不是锁失效了,而是锁功能和事务维度的问题;锁限定的是线程,也叫线程锁,而事务的提交和回滚是在数据库那一套系统中完成的,因此锁并不能锁住数据库的事务,所以才会导致查询时事务可能还没有提交,从而出现判断失效的现象。
那么应该如何修正呢?
最优的方案是使用数据库自带的事务锁!
代码语言:javascript复制@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
上述代码中所使用的是事务注解参数,是新开事务,当查询,变更数据同时存在时,会放入不同的事务组,因此事务不同步,要改成只使用@Transactional,并且可以去掉外层的syn同步锁。
完整代码如下:
代码语言:javascript复制@Transactional
public ResultVo<String> relationDept(String data) {
//校验是否已经绑定此部门
int row = dataSubscribeDao.selectCountRDepartmentWorker(deptFkCode, workerFkCode);
if (row == 0) {
RDepartmentWorker rDepartmentWorker = new RDepartmentWorker();
rDepartmentWorker.setDepartmentFkCode(deptFkCode);
// …… 省略其它赋值代码
dao.insertRDepartmentWorker(rDepartmentWorker);
//加入正常部门后,要删除加入的本校区默认部门
Long defaultDepFkCode = dao.getDefaultDepBySchool(schoolFkCode);
if (defaultDepFkCode != null) {
dao.deleteWorkerDefaultDepartment(workerFkCode, defaultDepFkCode);
}
}
原理就是让代码中的查询,插入数据放入同一组事务中,它们会一同提交。当并发请求来临的时候,由于查询和插入数据在同一个事务组,所以事务没提交的时候,再次查询的话,数据库就会让它等待,直到事务提交,这个时候就能查出新的数据了,于是就完成了并发锁的功能。