1、忘记释放锁:在申请锁和释放锁之间直接return
2、单线程重复申请锁:一个线程,刚出临界区,又去申请资源。
3、多线程多锁申请:两个线程,两个锁,他们都已经申请了一个锁了,都想申请对方的锁
4、环形锁的申请:多个线程申请锁的顺序形成相互依赖的环形
可剥夺资源:即当某进程新的资源未满足时,释放已占有的资源(破坏不可剥夺条件)
资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)
2.避免死锁
在有些情况下死锁是可以避免的。下面介绍三种用于避免死锁的技术:
1、加锁顺序(线程按照一定的顺序加锁)
2、加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
3、死锁检测
加锁顺序:当多个线程需要相同的一些锁,但是按照不同的顺序加锁,就会出现死锁现象。所以,如果能够确保所有的线程都按照相同的顺序获得锁,那么就会避免死锁现象。按照顺序加锁是一种有效的死锁预防机制,但是,这种方式需要你提前知道所有可能会用到的锁。
加锁时限:另外一个可以避免死锁的方法是在尝试获取锁的时候加一个超时时间,这也就意味着在尝试获取锁的过程中若超过了这个时限该线程则放弃对该锁请求。若一个线程没有在给定的时限内成功获得所有需要的锁,则会进行回退并释放所有已经获得的锁,然后等待一段随机的时间再重试。这段随机的等待时间让其它线程有机会尝试获取相同的这些锁,并且让该应用在没有获得锁的时候可以继续运行
死锁检测:死锁检测是一个更好的死锁预防机制,它主要是针对那些不可能实现按序加锁并且锁超时也不可行的场景。每当一个线程获得了锁,会在线程和锁相关的数据结构中(map、graph等等)将其记下。除此之外,每当有线程请求锁,也需要记录在这个数据结构中。当一个线程请求锁失败时,这个线程可以遍历锁的关系图看看是否有死锁发生。
避免死锁可以使用银行家算法,该算法假设每个客户有可能在没有得到最大资源的情况下也能工作。银行家算法对每一个请求进行检查,如果它会导致不安全状态,则不满足该请求,否则便满足。检查状态是否安全是看是否有足够的资源满足距最大需求最近的客户。
按照银行家算法的思想,当进程请求资源时,系统将按如下原则分配系统资源:
(1) 当一个进程对资源的最大需求量不超过系统中的资源数时可以接纳该进程。
(2) 进程可以分期请求资源,当请求的总数不能超过最大需求量。
(3) 当系统现有的资源不能满足进程尚需资源数时,对进程的请求可以推迟分配,但总能使进程在有限的时间里得到资源。
(4) 当系统现有的资源能满足进程尚需资源数时,必须测试系统现存的资源能否满足该进程尚需的最大资源数,若能满足则按当前的申请量分配资源,否则也要推迟分配。
对于每个线程都需要得到最大资源才能工作的情况下,避免死锁有如下的一些方法:
(1)请勿尝试在可能会对性能造成不良影响的长时间操作(如 I/O)中持有锁;
(2)请勿在可能直接或间接递归调用自己的函数里持有锁;
(3)一般情况下,请先使用粗粒度锁定方法,确定瓶颈,并在必要时添加细粒度锁定来缓解瓶颈。大多数锁定都是短期持有,而且很少出现争用。因此,请仅修复测得争用的那些锁定;
(4)使用多个锁定时,通过确保所有线程都按相同的顺序获取锁定来避免死锁;
(5)在等待某个资源时,使用超时机制;
(6)在系统中使用一个定时检查死锁环的机制,如果发现死锁就让一个线程的资源释放掉(数据库好像就是用这种方式)。