死锁

在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){
            int  fromLockA = System.identityHashCode(lockA);
            int  fromLockB = 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种死锁的类型。即:静态的锁顺序死锁,动态的锁顺序死锁,协作对象之间的死锁。在写代码时,要确保线程在获取多个锁时采用一致的顺序。同时,要避免在持有锁的情况下调用外部方法。

你可能感兴趣的:(死锁)