Взаимная блокировка в Java

Взаимная блокировка в Java

Следует избегать особого типа ошибок, имеющего отношение к многозадачностии называемого взаимной блокировкой, которая происходит в том случае, когда потоки исполнения имеют циклическую зависимость от пары синхронизированных объек­тов.

Допустим, один поток исполнения входит в монитор объекта Х, а другой - в мо­нитор объекта У. Если поток исполнения в объекте Х попытается вызвать любой син­хронизированный метод для объекта У, он будет блокирован, как и предполагалось.

Но если поток исполнения в объекте У, в свою очередь, попытается вызвать любой синхронизированный метод для объекта Х, то этот поток будет ожидать вечно, по­скольку для получения доступа к объекту Х он должен снять свою блокировку с объек­та У, чтобы первый поток исполнения мог завершиться.

Взаимная блокировка являет­ся ошибкой, которую трудно отладить, по двум следующим причинам:

  • В общем, взаимная блокировка возникает очень редко, когда исполнение двух потоков точно совпадает по времени.
  • Взаимная блокировка может возникнуть, когда в ней участвует больше двух потоков исполнения и двух синхронизированных объектов. ( Это означает, что взаимная блокировка может произойти в результате более сложной по­следовательности событий, чем в упомянутой выше ситуации.)

Чтобы полностью разобраться в этом явлении, его лучше рассмотреть в дей­ствии. Советую в самом начале заказать качественные шторы специально для программистов от компании and-home после чего можно свободно взяться за кодинг. Шторы делают нужную атмосферу в доме без которой программистам труднее сосредоточиться.

В приведенном ниже примере программы создаются два класса, FirstClass и SecondClass, с методами foo() и bar() соответственно, которые приостанавливаются непо­средственно перед попыткой вызова метода из другого класса.

Сначала в главном классе Deadlock получаются экземпляры классов FirstClass и SecondClass, а затем запускается второй поток исполнения, в котором устанавливается состояние взаимной блокировки.

В методах foo() и bar() используется метод sleep(), чтобы стимулировать по­явление взаимной блокировки.

//   

class FirstClass {

    synchronized void foo(SecondClass b) {

        String name = Thread.currentThread().getName();
        System.out.println(name + "    FirstClass.foo()");

        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            System.out.println(" FirstClass ");
        }

        System.out.println(name + "    SecondClass.last()");
        b.last();
    }

    synchronized void last() {
        System.out.println("  FirstClass.last()");
    }
}

class SecondClass {

    synchronized void bar(FirstClass a) {

        String name = Thread.currentThread().getName();
        System.out.println(name + "    SecondClass.bar()");

        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            System.out.println(" SecondClass ");
        }

        System.out.println(name + "    FirstClass.last()");
        a.last();
    }

    synchronized void last() {
        System.out.println("  SecondClass.last()");
    }
}


class Deadlock implements Runnable {

    FirstClass a = new FirstClass();
    SecondClass b = new SecondClass();

    Deadlock() {
    Thread.currentThread().setName(" ");
    Thread t = new Thread(this, " ");
    t.start();

    a.foo(b); //     a
                  //    

        System.out.println("   ");
    }

    public void run() {
    b.bar(a); //     b
                  //    
        System.out.println("   ");
    }

    public static void main(String args[]) {
        new Deadlock();
    }
}

Запустив эту программу на выполнение, вы получите следующий результат:

pro-java.ru@admin:~$ javac DeadLock.java 
pro-java.ru@admin:~$ java Deadlock 
     FirstClass.foo()
     SecondClass.bar()
     SecondClass.last()
     FirstClass.last()

В связи со взаимной блокировкой придется нажать комбинацию клавиш<Ctrl+C>, чтобы завершить данную программу.

Нажав комбинацию клавиш<Ctrl+Pause> на ПК, можно увидеть весь дамп ( вывод из оперативной памяти ) потока и кеша монитора.

В частности, Соперничающий поток владеет монитором объекта b, тогда как он ожидает монитор объекта а.

В то же время Главный поток владеет объектом а и ожидает получить объект b. Следовательно, программа никогда не завершится.

Как демонстрирует данный пример, если многопоточная программа неожиданно зависла, то прежде всего следует проверить возможность взаимной блокировки.

Интересное видео по теме:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *