synchronized遇到Transactional时线程不安全/事务失效

183 阅读1分钟

问题提出

@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在被调用层。