
有 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 不了解你用的啥框架去连的什么数据库,初看,感觉触发的是超时锁,不是真死锁 真死锁得是两个进程,各自在其线程内 /事务内 pid1: 先获取 lock1do something再获取 lock2 pid2: 先获取 lock2do 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 里,那么你俩线程其实是共用同一个连接同一个事务,这样就肯定有死锁的情况了 |