前言
先说一下死锁的产生,假设一个场景:
账户A向账户B转账,与此同时,账户B也向账户A转账,代码如下,我们用细粒度锁,锁住多个互斥资源
public void transfer(Account target, int amount) {
synchronized (this) {
synchronized (target) {
this.balance -= amount;
target.balance += amount;
}
}
}
上面代码就可能出现如下死锁,通过有向图我们可以发现循环等待的情况:
出现的原因
只有当以下 4 个条件同时满足的情况下,才会出现死锁
- 互斥,共享资源 X 和 Y 只能被一个线程占用;
- 占有且等待,线程 T1 已经取得共享资源 X,在等待共享资源 Y 的时候,不释放资源 X;
- 不可抢占,其他线程不能强行抢占线程 T1 占有的资源;
- 循环等待,线程 T1 等待线程 T2 占有的资源,线程 T2 等待线程 T1 占有的资源;
解决方案
只要打破产生死锁的任意一个条件,就能避免发生死锁
- 互斥 这个条件通常都不可避免发生
- 占有且等待:可以一次性将所有互斥资源都申请到,再进行临界区操作
- 不可抢占:使用 synchronzied 关键字,只能让线程本身去释放持有资源,外界无法进行干涉,也就是说很有可能会发生死等的情况,jdk 还提供有更灵活的锁机制 lock
- 循环等待:例子中可以看到,两个线程是按照相反的顺序去申请资源,那么将多个线程申请资源的顺序统一就可以避免循环等待了