V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
1cming
V2EX  ›  Java

面试:“必然”引起“死锁”的一段代码。

  •  
  •   1cming · 2018-05-23 10:42:29 +08:00 · 9226 次点击
    这是一个创建于 2400 天前的主题,其中的信息可能已经有所发展或是发生改变。

    这个必然让我有点疑惑,A 等待 B,B 等待 A 这种还有意外会发生吗?求教。

    第 1 条附言  ·  2018-05-24 09:54:36 +08:00
    大家可以看一下#15 @ryd994 还有 #13 @geelaw 还有 #20 @mashiro233 这几位朋友的回答,再次感谢大家。
    QK8wAUi0yXBY1pT7
        1
    QK8wAUi0yXBY1pT7  
       2018-05-23 10:53:10 +08:00
    建议多翻一下《操作系统》和语文书。
    zhengxiaowai
        2
    zhengxiaowai  
       2018-05-23 10:55:21 +08:00
    分分钟 100 个例子
    luojianxhlxt
        3
    luojianxhlxt  
       2018-05-23 12:09:47 +08:00
    数据库里面很多,代码倒是没咋接触过
    Arsenal16
        4
    Arsenal16  
       2018-05-23 12:32:56 +08:00
    老哥看一下 实战 Java 高并发程序设计 4.5 有关死锁的问题 那一章节
    百分百死锁啊, 讲的还是挺详细的.
    这里贴代码排版会有问题, 老哥你可以直接去看一下书
    ldd
        5
    ldd  
       2018-05-23 12:57:38 +08:00
    这个是肯定会引起死锁的,这种情况在多节点的 RAC 上见得比较多,比如需要同时更新多个表时,不同的业务对这几张表的更新顺序不一致,就容易引发死锁。

    不过像 Oracl 这样企业级的软件都会有自动的解锁行为,有个时间阈值,超过后就会牺牲掉一个事物,达到解锁目的。所以实际业务中几乎不会出现长时间的死锁。
    q397064399
        6
    q397064399  
       2018-05-23 13:09:35 +08:00
    死锁的四个必要条件
    0Y89tX3MgR4I
        7
    0Y89tX3MgR4I  
       2018-05-23 13:33:07 +08:00 via Android   ❤️ 6
    是不是我语文不好,看不懂楼主想表达什么
    1cming
        8
    1cming  
    OP
       2018-05-23 13:33:23 +08:00
    @Arsenal16 好的谢谢我下午看一下
    GuuJiang
        9
    GuuJiang  
       2018-05-23 13:43:56 +08:00
    methodA() {
    synchronized(lockA) {
    Thread.sleep(10000);
    synchronized(lockB) {
    ...
    }
    }
    }

    methodB() {
    synchronized(lockB) {
    Thread.sleep(10000);
    synchronized(lockA) {
    ...
    }
    }
    }

    拿去不谢
    1cming
        10
    1cming  
    OP
       2018-05-23 13:44:14 +08:00
    @Arsenal16 这里我刚才看了 他举的例子也并没有特别的地方 难道是我多想了?


    @hxd
    @zhengxiaowai
    @ldd
    @0Y89tX3MgR4I
    对方的原话大意是:写一个“必然”会出现死锁的一段代码,我面过很多人,他们写的都不是严格意义上的死锁,都被我挑出来很多刺。
    鉴于此,我在思考是否有代码之外比如 OS 上的一些考虑?
    1cming
        11
    1cming  
    OP
       2018-05-23 13:46:08 +08:00
    对方是某一线大厂资深级别的技术 leader,感觉事情并不简单,所以才会来 V 站一问,下午我会更新一下结果。
    startar
        12
    startar  
       2018-05-23 14:17:55 +08:00 via Android
    线程 1:
    锁 A
    锁 B
    干活
    释放 B
    释放 A

    线程 2:
    锁 B
    锁 A
    干活
    释放 A
    释放 B

    这样就会死锁啊
    geelaw
        13
    geelaw  
       2018-05-23 14:27:00 +08:00 via iPhone   ❤️ 2
    用睡眠并不会必然导致死锁,只是以非常高的概率导致而已。

    一个简单的方法是这样:线程 1 把自己的 handle 存在全局变量的 1 里,然后启动线程 2 并获得其 handle,然后等待这个 handle (等线程 2 结束);线程 2 的惟一工作是等待线程 1 的 handle。很容易证明,无论怎样调度,一定会进入死锁。

    在没有单进程多线程概念的操作系统(如传统 UNIX ),你需要通过进程的等待完成类似任务。
    ryd994
        14
    ryd994  
       2018-05-23 14:49:33 +08:00 via Android
    非重入锁重入就是保证死锁
    ryd994
        15
    ryd994  
       2018-05-23 14:57:42 +08:00 via Android   ❤️ 4
    多线程的话可以用信号量
    1 锁 A,2 锁 B
    1 给 2 发信号,2 给 1 发信号
    等到收到信号,1 锁 B,2 锁 A
    因为各自没有取得锁前不会发出信号,因此收到信号就可以保证死锁
    信号量可以使用操作系统的信号量,也可以使用原子变量简单实现

    这不是一道考锁的题,而是一道考分布式系统事件排序的题

    @GuuJiang 如果因为进程调度,method B 被调度到 A 完成之后才执行呢?正常调度系统会考虑公平性,所以很难发生。但这并不是前提条件。
    roychan
        16
    roychan  
       2018-05-23 14:58:44 +08:00
    写个哲学家就餐问题不就 ok 了。。。
    John60676
        17
    John60676  
       2018-05-23 15:02:13 +08:00
    哲学家就餐 加一
    0Y89tX3MgR4I
        18
    0Y89tX3MgR4I  
       2018-05-23 15:04:12 +08:00
    @ryd994 嗯,两个锁和一个条件变量就好了
    nullcc
        19
    nullcc  
       2018-05-23 15:05:39 +08:00
    thread 1:
    acquire(A)
    acquire(B)

    thread 2:
    acquire(B)
    acquire(A)

    Bomb !!!
    mashiro233
        20
    mashiro233  
       2018-05-23 15:27:16 +08:00   ❤️ 1
    楼上的一些朋友回答的很好了。
    调度器的运作原理对应用层来说应该是未定义的。代码中启动 A,B 两个线程,调度器可以不保证 A 在 B 之前或者之后启动,启动了也不保证每个线程能用多少 cpu 时间。
    ryd994
        21
    ryd994  
       2018-05-23 15:39:21 +08:00 via Android
    @roychan
    @John60676 哲学家就餐只是可能死锁,并不能保证死锁。如果某个哲学家手特别快,在别人反应过来前就左右都抢到了呢?有调度器在,就不能忽略这种可能。

    @nullcc A 特别快,直接把两个锁都拿到了
    1cming
        22
    1cming  
    OP
       2018-05-23 15:55:03 +08:00
    @geelaw
    @ryd994
    感谢二位,这个是我想要的答案。
    zhangchioulin
        23
    zhangchioulin  
       2018-05-23 16:11:16 +08:00
    iOS 中 OSSpinLock 优先级反转也会死锁
    USNaWen
        24
    USNaWen  
       2018-05-23 16:47:22 +08:00
    互斥
    不可剥夺
    请求和保持
    循环等待
    tangweihua163
        25
    tangweihua163  
       2018-05-23 19:02:07 +08:00
    必然死锁,一个线程内才行,连续多次试锁同一资源?多个线程就不是必然
    zhengxiaowai
        26
    zhengxiaowai  
       2018-05-23 21:37:00 +08:00
    @1cming 一个很经典的案例:哲学家就餐问题
    hand515
        27
    hand515  
       2018-05-23 22:03:26 +08:00
    线程 A:
    l1.lock()
    while(true)
    l2.lock()

    线程 B:
    l2.lock()
    while(true)
    l1.lock()
    KIDJourney
        28
    KIDJourney  
       2018-05-23 22:40:37 +08:00
    l1 = lock()
    l1.lock()
    l1.lock()

    这不就死锁了。
    tempdban
        29
    tempdban  
       2018-05-23 22:55:46 +08:00 via Android
    上了一课
    ryd994
        30
    ryd994  
       2018-05-24 02:52:09 +08:00 via Android   ❤️ 1
    @KIDJourney 你这个就叫不可重入锁重入
    如果是重入锁,同一个线程锁两次是允许的
    另外也可以设置如果重入则报错
    jedihy
        31
    jedihy  
       2018-05-24 03:06:36 +08:00
    对,这不是考 OS,这是靠分布式系统与算法
    ebingtel
        32
    ebingtel  
       2018-05-24 09:11:27 +08:00
    @q397064399 对头 lz 描述的情况 只满足了一个
    lerosua
        33
    lerosua  
       2018-05-27 13:23:16 +08:00
    邮局寄个包裹给你, 要你身份证就能给你,但你的身份证就在包裹里~ 所以死锁了~
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4939 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 09:50 · PVG 17:50 · LAX 01:50 · JFK 04:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.