有一段业务代码,不知道会不会死锁 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
dengji85
V2EX    程序员

有一段业务代码,不知道会不会死锁

  •  
  •   dengji85 2023-02-07 18:32:52 +08:00 1874 次点击
    这是一个创建于 1055 天前的主题,其中的信息可能已经有所发展或是发生改变。

    线上环境偶尔一个待办无法处理,服务器 cpu100%,原因时数据库死锁了,因为逻辑是这样

    有 2 条 id 分别为 1 ,2 的待办数据 处理 1 后,自动启一个线程更新 2 处理 2 后,自动启一个线程更新 1 因为处理比较耗时,用户可能反复重试,看起来会有死锁,但是自动更新是异步,不在一个事务里,这种情况会产生死锁吗? 
    service{ update(){ //事务 //耗时较长 } } WebControllerMethod(){ update(id:1) ThreadPoolTaskExecutor.execute(() -> { update(id:2) } } 
    5 条回复    2023-02-08 15:36:46 +08:00
    googlefans
        1
    googlefans  
       2023-02-07 19:01:27 +08:00
    去问问 chatgpt
    xmarsman
        2
    xmarsman  
       2023-02-08 09:46:28 +08:00
    chatgpt 的回复如下:
    ----------------------------------------------------------------------------
    上面的代码存在以下问题:

    update 方法没有返回值,但却使用了 return 语句。
    ThreadPoolTaskExecutor.execute 中没有对 update 方法进行调用,而是直接把 update 当作了一个代码块,这是错误的。
    缺少括号,在 ThreadPoolTaskExecutor.execute 后面的代码是无法正常编译的。
    Jtyczc
        3
    Jtyczc  
       2023-02-08 12:03:46 +08:00 via Android
    增加一个中间状态,处理中,然后开始处理前,判断一下异步更新是否处理中,如果处理中就告诉用户先等待,如果没有,去更新。
    tairan2006
        4
    tairan2006  
       2023-02-08 13:35:03 +08:00
    update(id:1)和 update(id:2)会有并发,如果你只是加行锁,两边应该没啥关联,当然具体的情况需要看业务逻辑。

    如果用户反复尝试,你应该第二次直接报错,而不是走到业务逻辑里。可以通过 redis 搞个分布式锁,或者显式的 select for update 加锁(MySQL 的话)。
    lookStupiToForce
        5
    lookStupiToForce  
       2023-02-08 15:36:46 +08:00   1
    不了解你用的啥框架去连的什么数据库,初看,感觉触发的是超时锁,不是真死锁

    真死锁得是两个进程,各自在其线程内 /事务内
    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 里,那么你俩线程其实是共用同一个连接同一个事务,这样就肯定有死锁的情况了
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5451 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 03:36 PVG 11:36 LAX 19:36 JFK 22:36
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86