概念
如果一个进程集合中的每个进程都在等待只能由此集合中的其他进程才能引发的事件,而无限期陷入僵持的局面称为死锁。
死锁产生的必要条件
互斥条件
临界资源是独占资源,进程应互斥且排他的使用这些资源。
占有和等待条件
进程在请求资源得不到满足而等待时,不释放已占有资源。
不剥夺条件
又称不可抢占,已获资源只能由进程自愿释放,不允许被其他进程剥夺。
循环等待条件
又称环路条件,存在循环等待链,其中,每个进程都在等待链中等待下一个进程所持有的资源,造成这组进程处于永远等待状态。
死锁产生的原因
- 当前线程拥有其他线程需要的资源
- 当前线程等待其他线程已拥有的资源
- 都不放弃自己拥有的资源
死锁又有哪几种?
- 锁顺序死锁
最简单最常见的死锁:
- 线程 A 调用 leftRight()方法,得到 left 锁
- 同时线程 B 调用 rightLeft()方法,得到 right 锁
- 线程 A 和线程 B 都继续执行,此时线程 A 需要 right 锁才能继续往下执行。此时线程 B 需要 left 锁才能继续往下执行。
- 但是:线程 A 的 left 锁并没有释放,线程 B 的 right 锁也没有释放。
- 所以他们都只能等待,而这种等待是无期限的–>永久等待–>死锁
- 动态锁顺序死锁
上面的代码看起来是没有问题的:锁定两个账户来判断余额是否充足才进行转账!
但是,同样有可能会发生死锁:
- 如果两个线程同时调用 transferMoney()
- 线程 A 从 X 账户向 Y 账户转账
- 线程 B 从账户 Y 向账户 X 转账
- 那么就会发生死锁。
- 协作对象之间发生死锁
上面的 getImage()
和 setLocation(Point location)
都需要获取两个锁的
并且在操作途中是没有释放锁的
这就是隐式获取两个锁(对象之间协作)..
这种方式也很容易就造成死锁…..
死锁解决的方法
主要有一下三种方法:
- 固定加锁的顺序(针对锁顺序死锁)
- 开放调用(针对对象之间协作造成的死锁)
- 使用定时锁–>tryLock()
- 如果等待获取锁时间超时,则抛出异常而不是一直等待!
总结
发生死锁的原因主要由于:
线程之间交错执行
- 解决:以固定的顺序加锁
执行某方法时就需要持有锁,且不释放
- 解决:缩减同步代码块范围,最好仅操作共享变量时才加锁
永久等待
- 解决:使用 tryLock()定时锁,超过时限则返回错误信息