死锁

【死锁】在JAVA编程中,有3种典型的死锁类型:

  • 静态的锁顺序死锁
  • 动态的锁顺序死锁
  • 协作对象之间发生的死锁
静态的锁顺序死锁 a和b两个方法都需要获得A锁和B锁。一个线程执行a方法且已经获得了A锁,在等待B锁;另一个线程执行了b方法且已经获得了B锁,在等待A锁。这种状态,就是发生了静态的锁顺序死锁。
//可能发生静态锁顺序死锁的代码 static class StaticLockOrderDeadLock{ private final Object lockA=new Object(); private final Object lockB=new Object(); public void a(){ synchronized (lockA) { //... //线程1 运行到这里,持有lockA,等待lockB synchronized (lockB) { System.out.println("function a"); } } }public void b(){ synchronized (lockB) { //... //线程2 运行到这里,持有lockB,等待lockA synchronized (lockA) { System.out.println("function b"); } } } }

  • 解决静态的锁顺序死锁的方法就是:所有需要多个锁的线程,都要以相同的顺序来获得锁。
//正确的代码 static class StaticLockOrderDeadLock2{ private final Object lockA=new Object(); private final Object lockB=new Object(); public void a(){ synchronized (lockA) { synchronized (lockB) { System.out.println("function a"); } } }public void b(){ synchronized (lockA) { synchronized (lockB) { System.out.println("function b"); } } } }

  • 动态的锁顺序死锁解决方案如下:使用System.identifyHashCode()返回hashcode的大小来保证锁的顺序。确保所有的线程都以相同的顺序获得锁
//正确的代码 static class DynamicLockOrderDeadLock2{private final Object myLock=new Object(); public void method(Object lockA, Object lockB){ intfromLockA = System.identityHashCode(lockA); intfromLockB = System.identityHashCode(lockB); if(fromLockA < fromLockB){ getResource(lockA, lockB); }else if(fromLockA > fromLockB){ getResource(lockB, lockA); }else { synchronized (myLock) { getResource(lockA, lockB); } } }private void getResource(Object lockA, Object lockB) { synchronized (lockA) { synchronized (lockB){ System.out.println("DynamicLockOrderDeadLock -- " + index); index++; } } } }

协作对象之间发生的死锁: 有时,死锁并不会那么明显,比如两个相互协作的类之间的死锁,比如下面的代码:一个线程调用了ModelA对象的setA方法,另一个线程调用了ModelB对象的method方法。此时可能会发生,第一个线程持有ModelA对象锁并等待ModelB对象锁,另一个线程持有ModelB对象锁并等待ModelA对象锁。
//可能发生死锁 static class ModelA{private int a; private ModelB modelB; public ModelA(ModelB modelB) { this.modelB = modelB; }public synchronized void setA(int a) { this.a = a; modelB.addModelA(this); }public synchronized int getA() { return a; }@Override public String toString() { return "ModelA{" + "a=" + a + '}'; } }static class ModelB{private final List modelAS = new ArrayList<>(); public synchronized void addModelA(ModelA modelA){ modelAS.add(modelA); }public synchronized void method(){ for (ModelA modelA:modelAS) { System.out.println(" a: " + modelA.getA()); } } }

上面的代码中,我们在持有锁的情况下调用了外部的方法,这是非常危险的(可能发生死锁)。为了避免这种危险的情况发生,我们使用开放调用。如果调用某个外部方法时不需要持有锁,我们称之为开放调用。
总结 综上,是常见的3种死锁的类型。即:静态的锁顺序死锁,动态的锁顺序死锁,协作对象之间的死锁。在写代码时,要确保线程在获取多个锁时采用一致的顺序。同时,要避免在持有锁的情况下调用外部方法。

    推荐阅读