有 2 条 id 分别为 1 ,2 的待办数据
处理 1 后,自动启一个线程更新 2
处理 2 后,自动启一个线程更新 1
因为处理比较耗时,用户可能反复重试,看起来会有死锁,但是自动更新是异步,不在一个事务里,这种情况会产生死锁吗?
service{
update(){
//事务
//耗时较长
}
}
WebControllerMethod(){
update(id:1)
ThreadPoolTaskExecutor.execute(() -> {
update(id:2)
}
}
1
googlefans 2023-02-07 19:01:27 +08:00
去问问 chatgpt
|
2
xmarsman 2023-02-08 09:46:28 +08:00
chatgpt 的回复如下:
---------------------------------------------------------------------------- 上面的代码存在以下问题: update 方法没有返回值,但却使用了 return 语句。 ThreadPoolTaskExecutor.execute 中没有对 update 方法进行调用,而是直接把 update 当作了一个代码块,这是错误的。 缺少括号,在 ThreadPoolTaskExecutor.execute 后面的代码是无法正常编译的。 |
3
Jtyczc 2023-02-08 12:03:46 +08:00 via Android
增加一个中间状态,处理中,然后开始处理前,判断一下异步更新是否处理中,如果处理中就告诉用户先等待,如果没有,去更新。
|
4
tairan2006 2023-02-08 13:35:03 +08:00
update(id:1)和 update(id:2)会有并发,如果你只是加行锁,两边应该没啥关联,当然具体的情况需要看业务逻辑。
如果用户反复尝试,你应该第二次直接报错,而不是走到业务逻辑里。可以通过 redis 搞个分布式锁,或者显式的 select for update 加锁(MySQL 的话)。 |
5
lookStupiToForce 2023-02-08 15:36:46 +08:00 1
不了解你用的啥框架去连的什么数据库,初看,感觉触发的是超时锁,不是真死锁
真死锁得是两个进程,各自在其线程内 /事务内 pid1: 先获取 lock1——do something——再获取 lock2 pid2: 先获取 lock2——do something——再获取 lock1 然后 pid1 因为 pid2 锁住了 lock2 ,导致 pid1 无法进入后续步骤,无法结束,进而 [无法释放 lock1] , 进一步导致 pid2 无法获得 lock1 也无法结束无法释放 lock2 。 ( pid1 try lock2 -- failed because pid2 hold lock2 -- pid1 keep holding lock1 -- pid2 try lock1 and failed -- pid2 keep holding lock2 -- pid1 try lock2......) 产生死锁的点在于,pid1 和 pid2 在不能获取第二步骤的锁( pid1 拿 lock2 ,pid2 拿 lock1 )的时候, [无法释放第一个锁] 。 但如果 WebControllerMethod(){ update(id:1) ThreadPoolTaskExecutor.execute(() -> { update(id:2) } } 里的 update(id:1) 可以在执行 ThreadPoolTaskExecutor.execute(() -> {update(id:2)} 前,就释放 id:1 的锁,那理论上就不存在死锁了 所以怀疑是超时锁,因为许许多多因为用户反复重试导致的 pid1 在不停 lock2 and update 2 ,导致你新的 pid2 lock2 fail 进而报了超时。 除非有种可能,你的 commit/rollback 不在 update 方法里,而在 WebControllerMethod 里,那么你俩线程其实是共用同一个连接同一个事务,这样就肯定有死锁的情况了 |