Тупики и синхронизированные методы - PullRequest
5 голосов
/ 07 июня 2011

Я нашел один из кодов переполнения стека, и я подумал, что он очень похож на то, с чем я сталкиваюсь, но я до сих пор не понимаю, почему это зашло в тупик.Пример был взят из Обнаружение взаимоблокировки в Java :

Class A
{
  synchronized void methodA(B b)
  {
    b.last();
  }

  synchronized void last()
  {
    System.out.println(“ Inside A.last()”);
  }
}

Class B
{
  synchronized void methodB(A a)
  {
    a.last();
  }

  synchronized void last()
  {
    System.out.println(“ Inside B.last()”);
  }
}

Class Deadlock implements Runnable 
{
  A a = new A(); 
  B b = new B();

  // Constructor
  Deadlock()
  {
    Thread t = new Thread(this); 
    t.start();
    a.methodA(b);
  }

  public void run()
  {
    b.methodB(a);
  }

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

В этом случае, когда вызывается конструктор Deadlock (), он запускается как поток.Когда это происходит, вызывается метод run ().Он вызовет b.methodB (a), который затем вызовет a.last (), чтобы просто распечатать инструкцию.В то же время a.methodA (b) будет вызывать b.last ().Нет перекрестных зависимостей ни на одном объекте, и они не выполняют метод одновременно.Даже если бы они были, синхронизированное ключевое слово поставило бы их в очередь, не так ли?Но почему же это иногда может зайти в тупик?Это не всегда, но иногда оно заходит в тупик, что совершенно непредсказуемо.Что вызывает этот тупик и обходные пути?

Ответы [ 3 ]

18 голосов
/ 07 июня 2011

Возможно, что выполнение этих двух операторов переплетено:

Thread 1:  a.methodA(b);    //inside the constructor
Thread 2:  b.methodB(a);    //inside run()

для выполнения a.methodA(), поток 1 должен получить блокировку для объекта A.

для выполнения b.methodB(), поток 2 должен получить блокировку для объекта B.

Чтобы поток 101 * * мог затем вызывать синхронизированный метод для экземпляра b, ему необходимо получить блокировку для b, удерживаемую потоком 2, что заставит поток 1 ждать, пока этот замок освобожден.

Для того чтобы methodB() в Thread2 мог вызывать синхронизированный метод в экземпляре a, ему необходимо получить блокировку, удерживаемую в a потоком 1, что также заставит поток 2 ожидать.

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

Важно понимать, что этот код не будет вызывать взаимоблокировку в 100% случаев, когда вы его выполняете - только при выполнении четырех важных шагов (Thread1 удерживает блокировку A и пытается получить B, а Thread 2 удерживает блокировку B и пытается получить а) выполняются в определенном порядке. Запустите этот код достаточно много раз, и этот порядок обязательно произойдет.

2 голосов
/ 07 июня 2011

synchronized помещает блокировку на объект , который должен быть получен до выполнения методов или кодовых блоков.Поскольку он блокирует целые объекты, это неэффективный инструмент, который иногда выглядит довольно простым в использовании, но дает такие взаимоблокировки, когда никакие реальные оспариваемые данные не читаются и не записываются.объект.b.method(a) блокирует объект b.И ни один поток выполнения не может продолжать вызывать b.last() или a.last(), потому что они оба ждут, пока другой объект освободит свою блокировку.

0 голосов
/ 07 июня 2011

Вызов метода A (эффективно) блокирует (a), блокирует (b).Если задача затем переключается и пытается метод B, она сразу же нажимает на блокировку (b).

...