死锁原理的分析

在程序中可以把执行变更抽象为对某种对象的状态的变更。在多线程环境下,如果一个状态无法在单个指令中完成,那么多个线程同时变更该状态可能导致该状态的不一致。

Demo案列

我们这里还用熟悉的转账案列。

A给B 转账 50元

这里边要执行的操作是是从A 的余额 S(A) - 50, 同时B 的余额 S(B) + 50。 由于在计算获取到S(A),S(B)后,A 账户余额还可能发生变化(比如A设定了每月信用卡自动还款),如果这时还是用S(A) 进行计算,就放生了错误。所以为了保证在转账的过程中S(A), S(B) 是不变的, 我们需要在转账开始的时候,对A, B 账户同时枷锁, 这样保证了在转账结束前,没有其他操作导致S(A), S(B)发生变化,我们就可以安全的使用S(A), S(B) 进行计算了。

A给B转账50元, B恰好同时给A 转账100元

A 转账的操作 1,Lock(A) 2,Lock(B) 3, 转账操作 4,unlock(B) , 5unlock(A)

B 转帐的操作 1,Lock (B) 2,lock( A ) 3,转账操作 4,unlock(A ), 5,unlock(B)

A,B 要给对方转账都需要4个步骤

  • 如果 A 在早上转账, B 在下午转账, 不会有问题
  • 如果A,B 几乎同时在给对方转账, 同时结束了步骤一,开始到步骤2, 这时候死锁便产生了。

死锁产生的条件

  • 一个加锁操作中需要使用多个锁的参与
    如果操作中不需要多个操作,案例中A,B 如果只需要一个锁,那么死锁是可以避免的。

  • 锁是排它的(exclusive)
    如果操作中加锁操作是允许多个操作同时进行的(Share lock), 那么也可避免死锁。

  • 操作对锁的使用方式是 HOLD_AND_WAIT.
    如果在A在lock(B) 失败时,变放弃操作,等待, 那么也可以避免死锁

  • 操作中对锁的使用形成环路
    如果A,B 转账过程中都是用了 1 lock(A), 2 lock(B) 的顺序,没有形成环路,也可以避免产生死锁

避免死锁

针对死锁产生的条件, 可以使用一些规避方案

  • 使用一个锁来执行操作
    在案例中我们可以使用一个“转账锁”,不论A转账,或者B 转账都是用同样一个锁(当然这不是好的方案)
  • 使用共享锁
    这个案列中不太合适
  • 使用放弃的策略
    如果A 转账,lock(B) 失败, 便放弃已经获得的A的锁( 这个可能导致live lock)
  • 使用一致的所顺序
    在案列中,可以多账户id 排序,然后转账过程中都是用这套所顺序

猜你喜欢

转载自blog.csdn.net/taozhen1987/article/details/80719043