java事务下并发问题怎么搞

现在业务中在实现类serviceImpl.aaaa()方法(事务在该方法上)中有相关业务
业务大概就是

查库中字段sta='R'的数据--》处理逻辑--》修改数据将字段sta改为='I'

现在出现得问题就是多个线程进入后

多个线程同时去请求,**在查数据过程中由于并发获取到了同样的数据**
然后再通过同样逻辑后 去update数据是就发生了相互覆盖得情况,后执行得把前面执行的给了覆盖了,最终相当于数据中只有一条修改语句而不是多条。
各位这种要怎么解决。

不能加锁么?加个锁试试呢?

可以给这个字段加上锁,每次修改这个字段的时候,先获取锁,然后才能修改。
简单一点,可以用sql语句的的case when then end 方法,只有当当前数据是R的时候才修改成I,否则不变。

我们可以举秒杀场景。秒杀场景本质上是一个减库存的操作›当前库存只有100,现在有两个线程开启两个事务。
第一个线程A:
begin;
select * from test;

第二个线程B:
begin;
select * from test;

两个线程获取的值都是100。
然后两个线程执行:update test set v = v - 1 where v = 100;
假如A线程先执行,它会先加锁(至于是行级锁还是表级锁取决于v是否走索引),直到事务提交在释放锁。(这里是关键)
然后B线程执行,这时候只要A线程不提交事务,B线程就会被阻塞住。
A执行完成或者回滚,释放掉锁,B线程继续执行。

回到你的问题:
1、重复覆盖,这个问题比较容易解决。我们可以使用乐观锁解决,也就是在update中添加一个where判断,就可以解决重复覆盖的问题。
2、update操作的时候回加独占锁(因为mysql内部使用了MVCC,所以读操作不会阻塞),而且直到commit才会释放这把锁,这时候所有的update操作都会被阻塞住,这里是我们最关心的问题。如果走了索引,所有的事务都会阻塞到这一个点,如果不走索引,这个表的所有update操作都会被阻塞住。所以在秒杀的场景下资源消耗才是问题。

update操作会针对索引进行加锁,这时候update ... set ... where中的where是最真实的数据,可以通过这个解决数据覆盖的问题

重复覆盖,这个问题比较容易解决。我们可以使用乐观锁解决,也就是在update中添加一个where判断,就可以解决重复覆盖的问题。

只需要加where判断就行了,每次更新判定sta的值是否变化,若没有变化则更新,变化则不更新

加个乐观锁,也就是在表上加一个字段 rowVersion 初始值为 0 ; 每次更新数据的时候都要根据当前的rowVersion更新且需同步更新此值,rowVersion = rowVersion+1;
即: update table set sta='I',rowVersion = rowVersion+1 where sta ='R' and rowVersion = #{rowVersion};

如果所有线程干的事都是相同的,那最简单的就是把并发控制交给数据库去做,sql写成 update XX set sta='T' where sta='R'
当然,也可以在java里面通过各种方式来,synchronized、lock等等

乐观锁+触发器
乐观锁如前面所说,在表上加version列,代表该记录的版本,需要update的时候首先读出该记录及其version字段,然后更新记录时version字段在程序中先+1,然后在sql语句中set version为+1后的值。
同时表上加上update触发器,每次更新校验当前记录的version new - version old = 1?不是就抛错。这样避免了每次更新需要在where条件加上version的判断。