问题提出
@Transactional
public synchronized void aMethod(){
// ...
}
上面的方法里既有事务,又有锁,这么写有没有什么问题?
有。上面方法的执行顺序是这样的:
获取锁 -> 进入方法 -> 遇到第一句SQL时开启事务 -> 执行业务代码 -> 释放锁 -> 方法结束 -> 提交事务。
那么在释放锁后,提交事务前这段时间,就可能有别的线程获取到锁进入方法,因此线程不安全。
思考
那改成这样的代码还有没有问题?
public synchronized void bMethod(){
aMethod();
}
@Transactional
public void aMethod(){
// ...
}
只要让aMethod的事务先提交了就行了嘛。
这个写法行不行?我在网络上查到的很多帖子都这么写。
答案是不行。这种写法线程是安全了,但是Transactional失效了。原因是调用同一个实例里的方法是不会开启事务的,只有在别的类的实例里调用aMethod才会开启事务。
解决办法1
public synchronized void bMethod(){
service.aMethod();
}
@Transactional
public void aMethod(){
// ...
}
这么写,自己注入自己,可以解决。
解决办法2
做好代码分层,比如在调用这个service的controller层去加锁,在service里加事务。synchronized在调用层,transcational在被调用层。