springboot与锁

1.应用场景

开发过程中经常遇到多线程更新数据实体,例如库存管理(入库与出库,出库与出库)等。

多线程更新数据实体会造成数据覆盖,造成bug.

2.场景模拟

batch(){
    for(15){
        new Thread(()->{test()}).start()
    }
}

test(){
    entity = findById(10L)
    entity.setNum(entity.getNum()-1)
    save(entity);
}

初始化库存100,运行结束库存90。

3.解决方案

可以从两方面解决:应用层面和数据库层面。应用层面使用synchronize或ReentrantLock,数据库层面for update 或者 乐观锁。

4.锁

synchronize 修饰 test() 方法。

ReentrantLock 可重入锁,test() 使用的经典范式:

lock.lock()
try{
    //业务代码
}finally{
    lock.unlock()
}

synchronize 优化后性能和Lock 差不多;不过ReentrantLock更灵活,可以用非阻塞方式获取锁,可以响应中断,可以设置阻塞时间。ReentrantLock 可以使公平锁或非公平锁,synchronize只能是非公平锁。ReentrantLock是jdk提供的,synchronize是jvm提供的。

事务和锁发生的异常

如果test方法启用@Transational 可能会发生异常,因为Transational真正启动时是业务代码第一条sql语句,提交事务是在执行方法体后。执行过程

lock-->transational start -->unlock -->transaciontal commit ,无法保证事务的原子性。

可以改变执行过程 lock -->transational start -->transational end -->unlock

解决方案是:业务代码单独写一个方法,启动事务。

5.sql

(1)悲观锁

select * from table where id =#{id} for update

查询数据加行锁,更新完成,释放锁。注意id必须是索引,否则行锁会升级为表锁。

(2)乐观锁

在实体类和表加字段version;在更新之前比较版本号,如果版本号不同说明已经有更新了,更新失败,重新读取实体数据进行更新。

update client set num =#{num} ,version = version + 1 where id = #{id} and version = #{version}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值