死锁
每个人手里都有其他人需要的资源,自己又不会放下手上的资源,这么一直等待下去,就会发生死锁.
当一个线程永远占有一个锁,而其他线程尝试去获得这个锁,那么它们将永远被阻塞.
当线程A占有锁L时,想要获得锁M,同时线程B持有M,并尝试得到L,两个线程将永远等待下去,这种情况是死锁最简单的形式(或称致命的拥抱,deadly embrace)
数据库不会发生死锁的情况,它会选择一个牺牲者,强行释放锁,让程序可以继续执行下去.
JVM不行,只能重启程序.
死锁并不会每次都出现
死锁很少能立即发现.一个类如果有发生死锁的潜在可能并不意味着每次都将发生,它只发生在该发生的时候.
当死锁出现的时候,往往是遇到了最不幸的时候--- 在高负载下.
锁顺序死锁
public class LeftRightDeadLock { private Object leftLock = new Object(); private Object rightLock = new Object(); public void getLeftLock(){ synchronized (this.rightLock){ synchronized (this.leftLock){ //do something } } } public void getRightLock(){ synchronized (this.leftLock){ synchronized (this.rightLock){ //do something. } } } }两个线程分别进入getRightLock和getLeftLock方法,同时获得第一个锁,在等待下一个锁的时候,就会发生锁顺序死锁.
发生死锁的原因: 两个线程试图通过不同的顺序获得多个相同的锁.
如果请求的顺序相同就不会出现循环的锁依赖现象,就不会产生死锁了.
如果所有线程以通用的固定秩序获得锁,程序就不会出现锁顺序死锁问题了.动态的锁顺序死锁
public class DynamicDeadLock { public void transferMoney(Account fromAcount,Account toAccount){ synchronized (fromAcount){ synchronized (toAccount){ //转账操作 } } } }当两个线程同时调用transferMoney,一个从X向Y转账,另一个从Y向X转账,那就会发生死锁.
transferMoney(myAccount,yourAccount) transferMoney(yourAccount,myAccount)之前说了,造成死锁的原因就是以不同的顺序获得相同的锁.
那么要解决这个问题,我就就必须制定锁的顺序.
System.indentityHashCode(传入对象)方法可以得到对象的哈希码.我们通过哈希码来决定锁的顺序.
public class DynamicDeadLock { private Object obj = new Object(); public void transferMoney(Account fromAcount,Account toAccount){ //这个内部类秒啊,可以减少重复代码 class Helper { public void transferMoney(){ //真正的转账操作.. //假装使用 外部的两个参数 fromAcount和toAccount做一下操作.. } } //制定锁的顺序 int fromHash = System.identityHashCode(fromAcount); int toHash = System.identityHashCode(toAccount); if(fromHash<toHash){ synchronized (fromAcount){ synchronized (toAccount){ new Helper().transferMoney(); } } }else if(fromHash>toHash){ synchronized (toAccount){ synchronized (fromAcount){ new Helper().transferMoney(); } } }else{ //使用成员变量的锁 synchronized (obj){ synchronized (fromAcount){ synchronized (toAccount){ new Helper().transferMoney(); } } } } } } 虽然有点麻烦,但是减少了发生死锁的可能性.
注意上面代码的最后一种else的情况,使用了一个额外的obj的锁,这是因为极少数的情况下会出现hashcode相同的情况,当hashCode相同的时候,使用之前的两种顺序锁,两个线程同时调用两个方法,参数换位,颠倒顺序计算哈希值,就又有了出现死锁的可能,所以引入第三种锁来保证锁的顺序,从而减少死锁发生的可能性.
如果经常出现hash值冲突,那么并发性会降低(因为多加了一个锁),但是因为
System.identityHashCode的哈希冲突出现频率很低,所以这个技术以最小的代价,换来了最大的安全性.
如果Account具有一个唯一的,不可变的,并且具有可比性的key,比如账号,那么就可以通过账号来排定对象顺序,这样就能省去obj的锁了.
协作对象间的死锁
public class A { private final B b ; public A(B b) { this.b = b; } public synchronized void methodA(){ //do something. //调用B的同步的方法 b.methodB(); } } public
