java中事务中遇到锁会造成什么问题,以及该如何解决?

在spring中实现事务有多种方式,主要是两种:一种是声明式事务,一种是编程式事务,今天我们就讲声明式事务中的一种,使用注解@Transactional,这个注解的作用就是帮助我们在代码执行完毕之后自动提交事务,在发生异常时进行回滚。

比如以下这段代码:

@Transactional
public void updateDBInfo(List<Entity> entityList){
    //待更新的数据
    entityList.stream().foreach((arg) -> {
        arg.setModifier("xxx");
        arg.setModifierTime("xxxx-xx-xx hh:mm:ss");
    });
    this.updateBatchById(entityList);
}

 这是一段非常简单的代码段。假如我们就是需要加进行更新,但是如果这段代码需要涉及到锁的话,如果架构是分布式,也就是部署了多台service Application服务器,然后进行负债均衡,那么此时如果有多个用户同时操作的话,每个用户的请求会由于负债均衡可能到达不同的service Application服务器应用。所以这时候我们的锁用线程级别的锁是不行的,只能用进程级别的锁,也就是分布式锁。分布式锁的话可以用redis的那个命令叫setNx,这里我就不过多赘述了。主要讲在事务里面有锁会造成什么问题,以及如何解决该问题。

如果我们还是使用这个注解@Transactional,这个注解我们刚刚也说了。这个注解的作用就是帮助我们在代码执行完毕之后自动提交事务,在发生异常时进行回滚。

首先改造代码进行加锁:

@Transactional
public void updateDBInfo(List<Entity> entityList){
RLock lock = null;
try {
    lock = redissonTool.tryLock(RedisKeyConstants.REDIS_KEY_PURCHASE_ORDER_EXTEND, Codes.KEY_ERROR);
    //待更新的数据
    entityList.stream().foreach((arg) -> {
        arg.setModifier("xxx");
        arg.setModifierTime("xxxx-xx-xx hh:mm:ss");
    });
    this.updateBatchById(entityList);
} finally {
    redissonTool.unLock(lock);
}
}

 如果我们是这样写的话,那么会有什么问题呢?

当代码执行完毕,也就是finally里面的锁都释放了,去提交事务,但是提交事务也是需要时间的,有可能还没提交事务完,由于锁释放了,其他客户端就会拿到锁了,但是此时该用户感知不到此时DB数据库中的数据是最新的,也就是所谓的可见性,什么是可见性?

可见性:顾名思义就是一个线程的操作结果对其他所有线程都是可见的,因为每一个线程都共享同一个共享主内存。

那么这时候另一个用户在客户端就不知道这个DB的数据 是更改的,又会去更改,然后提交事务,这时候刚刚那个客户端的用户也提交事务成功了,那么就会造成脏数据了,数据不一致。

我给大家画个图展示:

但是由于我刚刚说的这种,A还没提交更新事务之后,锁释放,B拿到,B更新完,提交更新事务,成功改为2,A的提交更新事务完成。此时就还是1.就是脏数据了。

所以有什么办法解决呢?

那我们把自动事务注解@Transactional改成手动事务。让事务提交成功之后再去释放锁,而不是在锁释放之后再提交事务。

手动事务我用的是transactionTemplate.execute(TransactionCallback callback);然后用的是TransactionCallback接口的抽象实现类,用匿名内部类。

TransactionCallbackWithoutResult的抽象方法:doInTransactionWithoutResult

也就是:

transactionTemplate.execute(
        new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                //业务更新逻辑
            }
        });

总结:

        在事务中碰到锁,必须使用手动事务,让事务提交成功之后,再去释放锁。而非释放锁之后再提交事务。也就是不能使用@Transactional注解的自动提交事务。

一般模板:

RLock rLock = null;
try{
    rLock =  redissonTool.tryLock("REDIS_LOCK_NAME");
}catch(){
    transactionTemplate.execute(new TransactionCallbackWithoutResult(){
        protected void doInTransctionWithoutResult(){
            //业务更新逻辑;
        }
    })
}finally{
    rlock.unlock();
}

 最后:

        如果大家觉得这篇文章对你们有所帮助的话,麻烦点个免费的赞赞,谢谢,也祝各位码农在未来的IT道路上越走越远。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值