synchronized,@Transactional,开启事务和锁后两次数据读取结果相同

数据库中存在一张表,用于存储每个调查问卷的填报编号,填报编号连续且唯一,问卷填报后,在最终提交时, 调用service的接口,获取填报编号

    @Override
    @Transactional
	public synchronized String getQuestionAnswerNumber(String code) {
        Integer no = genNumberMapper.getQuestionAnswerNoByCode(code);
        if(no == null){
    		genNumberMapper.insertQuestionAnswerNoByCode(code);
    		no = 0;
    	}
    	GenNumber genNumber = new GenNumber();
    	genNumber.setStub(code);
    	genNumber.setNo(no + 1);
    	genNumberMapper.setQuestionAnswerNoByCode(genNumber);
    	return String.format("%06d", (no + 1));
	}

代码编写流程主要是,通过code关键字,获取填号,然后将编号加1存入到数据库,这样下一个人获取编号时,能够按照顺序递增。为了避免多线程情况下,填报编号错乱,使用synchronized关键字,为方法加锁。

然而在生产环境中,并发量大的情况下,导出数据时,填报编号还是重复了。。。。

我们已经在方法生加了锁,为何编号还会出现重复?
分析一:
pring 在使用@Transactional关键字开启事务时,在方法执行完毕前,事务没有提交,数据会缓存在mybatis的一级缓存中,当方法执行完后,数据再进行提交。可我们为方法加了锁,为何还是会产生重复的数据呢,经过推测与测试,可能是获取填报编号的方法已经执行了,但事物并没有提交,这个时候另一个线程已经已经进来,导致获取的数据不准确。

在这里插入图片描述
所以这里要打印一下sql,看是否调用数据库。

分析二:

应该是synchronized和@Transaction一起使用导致的。spring中对事务的处理,是采用动态代理的方式,事务里的方法是包含synchronized方法,导致有多个线程进入到事务中。

我们知道Spring事务的底层是Spring AOP,而Spring AOP的底层是动态代理技术。跟大家一起回顾一下动态代理:


    public static void main(String[] args) {

        // 目标对象
        Object target ;

        Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), Main.class, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                // 但凡带有@Transcational注解的方法都会被拦截

                // 1... 开启事务

                method.invoke(target);

                // 2... 提交事务

                return null;
            }
            
        });
    }

  1. 实际上Spring做的处理跟以上的思路是一样的,我们可以看一下TransactionAspectSupport类中invokeWithinTransaction():

    Spring事务管理是如何实现的

    调用方法前开启事务,调用方法后提交事务

    Spring事务和synchronized锁互斥问题

    在多线程环境下,就可能会出现:方法执行完了(synchronized代码块执行完了),事务还没提交,别的线程可以进入被synchronized修饰的方法,再读取的时候,读到的是还没提交事务的数据,这个数据不是最新的,所以就出现了这个问题。

    事务未提交,别的线程读取到旧数据

问题解决:

1.建议是锁上移,也就是说要包住整个事物单元。
2.去掉@Transactional

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值