关于@Transactional和synchronized使用的问题

引言

@Transactional是spring通过aop让我们轻松实现事务控制的一个注解;而synchronized是实现同步的java关键字;但是它们两个不能一起使用,一起使用会出现

synchronized失效的问题,这里简单记录一下这个问题;

发现问题

我在impl中实现一个功能逻辑时,为了保证幂等性,在方法中使用synchronized保证同一个用户短时间内多次请求只能串行执行,因为数据变动涉及多张表,所以我又加了一个@Transactional注解,伪代码如下:

@Transactional
public void demo() {
    ...一些操作...
    synchronized(lock) {
        ...数据库读写操作...
    }
}

然后测试时就发现synchronized没有生效,确认代码逻辑没有问题后,查询了资料发现了问题;

问题原因

这里不过多解释@Transactional注解底层,感兴趣可以自行查阅资料;

主要原因就是@Transactional注解通过aop实现事务管理,当标注该注解的方法执行完成后才提交事务,而synchronized代码块又是在一个事务内,就会出现第一个线程释放锁后但是事务还没提交,第二个线程就进入同步代码块获取到未提交的数据库数据;

大致如图:

image-20221014180919587

解决问题

大致思路

解决方法很简单,既然问题出在事务未提交,那么只要把对应事务操作的代码单独抽取出来,封装成一个单独的方法,在synchronized中调用该方法即可;

如图:

image-20221014181834783

伪代码为:

public void demo() {
    ...一些操作...
    synchronized(lock) {
        databaseOption(); // 调用数据库操作方法
    }
}

@Transactional
public void databaseOption() {
    ...数据库读写操作...
}

注意:@Transactional注解修饰的方法需要是public权限;

虽然这样写好像解决了事务未提交的问题,但是这样写会存在新的问题;上面这种是这两种方法都写在serviceImpl中,但是这样调用databaseOption方法就会出现@Transactional事务不生效的情况;


@Transactional事务不生效问题

所以在同一个类内部调用@Transactional标注的方法事务也不会开启,原因是:

@Transactional事务管理是基于动态代理对象的代理逻辑实现的,那么如果在类内部调用类内部的事务方法,这个调用事务方法的过程并不是通过代理对象来调用的,而是直接通过this对象来调用方法,绕过的代理对象,肯定就是没有代理逻辑了

依然是@Transactional的底层原理,可以好好研究一下这个注解,面试就有的聊了;


那么解决方法就是:

不要把由@Transactional修饰的databaseOption方法和调用它的方法放到同一个类中;这里你可以多写个service放databaseOption方法,但是这样好像没有什么意义;我选择的是把同步代码块放到controller中,在controller中调用serviceImpl中的databaseOption方法;

伪代码:

controller类

@RestController
public class TestController {
    @Resource 
    private TestService testService;
    
    @PostMapping("/test")
    public String testInterface() {
            ...一些操作...
        synchronized(lock) {
            testService.databaseOption(); // 调用数据库操作方法
        }
    }
}

service类

@Service("testService")
public class TestServiceImpl implements TestService {
    
    @Transactional
    public void databaseOption() {
        ...数据库读写操作...
    }
}

这样就能保证@Transactional事务生效;但是这样写的缺点就是一些逻辑会被拆分到controller中,可读性会稍差点;

总结

这就是这两天我踩的坑,总的来说所有问题的出现都是由于对@Transactional这个注解理解的不透彻,以后还是要了解该注解的实现原理;

  • 9
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
Transactionalsynchronized是实现事务控制和同步的两种机制。 @TransactionalSpring框架提供的注解,用于实现事务控制。它通过AOP(面向切面编程)实现,在方法执行完成后才提交事务。而synchronizedJava关键字,用于实现同步。它可以确保同一个对象的同步方法或代码块在同一时间只能被一个线程执行,以避免多线程并发访问时的数据竞争问题。 然而,这两个机制不能一起使用,因为@Transactional注解事务是通过AOP实现的,而synchronized是通过锁机制实现的。当@Transactional注解的方法执行完成后才提交事务,而synchronized代码块是在一个事务内执行的。因此,如果在一个方法中同时使用@Transactionalsynchronized,会出现第一个线程释放锁后但是事务还未提交,第二个线程就进入同步代码块获取到未提交的数据库数据的情况。这样可能会导致数据一致性的问题。所以,在使用事务控制和同步的时候,需要注意避免同时使用@Transactionalsynchronized。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [关于@Transactionalsynchronized使用问题](https://blog.csdn.net/YXXXYX/article/details/127325870)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YXXYX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值