Мертвые блокировки в Java: когда они возникают? - PullRequest
10 голосов
/ 30 августа 2010

Я работаю над приложением для J2ME, и иногда замораживает полностью , и требуется совсем немного времени , чтобы AMS закрыла его. Мне кажется, это проблема тупика.

Не могли бы вы посоветовать мне, что может вызвать мертвые запоры? Например, вызовет ли синхронизированный метод объекта мертвую блокировку, если он вызовет другой собственный синхронизированный метод?

Спасибо!


Обновление

Прав ли я, говоря, что тупик должен произойти в следующем случае:

Объект P вызывает метод синхронизации объекта A , который вызывает метод синхронизации объекта B , который вызывает метод синхронизации объекта A

Извините, если это выглядит глупо с моей стороны, скорее всего, это так. Но вот почему Я спрашиваю. Спасибо!

Ответы [ 4 ]

12 голосов
/ 30 августа 2010

Может ли, например, вызов синхронизированного метода объекта вызвать мертвую блокировку, если он вызывает другой собственный синхронизированный метод?

Нет, потому что synchronized блокировки в Java повторные : вы можете получить одну и ту же блокировку из одного и того же потока несколько раз без проблем.

возникает тупик, например когда поток A удерживает блокировку L и пытается получить блокировку M, тогда как поток B удерживает блокировку M и пытается захватить блокировку L. Таким образом, оба потока ожидают блокировки, удерживаемой другим, и не могут прогрессировать, чтобы снять свою собственную блокировку. Это заставляет оба потока ждать вечно. Ситуация может включать более 2 потоков.

Блокировки могут быть очень трудно обнаружить, поэтому типичным способом является попытка избежать их путем тщательного проектирования. Простейшее средство для достижения этого состоит в том, чтобы гарантировать, что любой поток, который должен получить несколько блокировок, получает их всегда в том же предопределенном глобальном порядке. Например. если в приведенном выше примере оба потока A и B попытаются сначала получить блокировку L, а затем заблокировать M, тупиковой блокировки не будет.

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

Обновление: доступ к замку снаружи объекта

При наличии встроенных блокировок Java (т. Е. synchronized блоков) сам базовый объект Lock не виден в коде, только объект, на который мы находимся. Рассмотрим

class MyClass {
  private Object object = new Object();

  public synchronized void synchronizedOnThis1() {
    ...
  }
  public void synchronizedOnThis2() {
    synchronized(this) {
      ...
    }
  }
  public void synchronizedOnPrivateObject() {
    synchronized(object) {
      ...
    }
  }
}

class ExternalParty {
  public void messUpLocks() {
    final MyClass myObject = new MyClass();
    synchronized(myObject) {
      Thread otherThread = new Thread() {
        public void run() {
            myObject.synchronizedOnThis1();
        }
      };
      otherThread.start();
      // do a lengthy calculation - this will block the other thread
    }
  }
}

Оба метода synchronizedOnThis* синхронизируются на содержащем экземпляре MyClass; синхронизация двух методов эквивалентна. Однако экземпляр класса, очевидно, доступен для внешнего мира, поэтому внешняя сторона может использовать его как блокировку извне класса, как показано выше. И если объект доступен из другого потока, и этот поток вызывает один из его synchronizedOnThis* методов, этот вызов будет блокироваться, пока этот поток находится в блоке synchronized(myObject).

OTOH метод synchronizedOnPrivateObject использует закрытый объект в качестве блокировки. Если этот объект каким-либо образом не опубликован сторонним лицам, никто другой не может (непреднамеренно или злонамеренно) вызвать тупик, связанный с этой блокировкой.

2 голосов
/ 30 августа 2010

Наиболее вероятная причина - два потока, пытающиеся получить блокировки для двух объектов.Поток 1 блокирует A и ожидает B, но поток 2 блокирует B и ожидает A. Оба потока в конечном итоге ждут объекты, которые никогда не будут освобождены.

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

Java 5 имеет явные объекты Lock, которые позволяют более детально управлять, включая тайм-ауты для простых синхронизированных блоков, но я не знаю, будут ли они полезны для J2ME.Имеется бэкпорт библиотек параллелизма Java 5, с которыми возможно работать J2ME - http://backport -jsr166.sourceforge.net / , если эта проблема достаточно велика, чтобы ее использовать.*

1 голос
/ 30 августа 2010
0 голосов
/ 30 июня 2015

когда возникает взаимоблокировка?

Чрезмерное использование синхронизации с непоследовательным упорядочением блокировки приводит к взаимоблокировке.

Решениечтобы избежать мертвой блокировки

Поддерживать порядок и использовать меньше синхронизации.

Ниже приведен один сценарий, который создает тупик.

    public void method1() {
            synchronized (String.class) {
                System.out.println("Aquired lock on String.class object");

                synchronized (Integer.class) {
                    System.out.println("Aquired lock on Integer.class object");
                }
            }
        }

        public void method2() {
            synchronized (Integer.class) {
                System.out.println("Aquired lock on Integer.class object");

                synchronized (String.class) {
                    System.out.println("Aquired lock on String.class object");
                }
            }
        }

Если method1 () и method2 () оба будут вызваны двумя или несколькими потоками, есть большая вероятность тупиковой ситуации, потому что, если thead 1 получает блокировку на объекте Sting при выполнении method1 (), а поток 2 получает блокировку на объекте Integer при выполнении method2() оба будут ожидать друг друга, чтобы снять блокировку с Integer и String для продолжения, что никогда не произойдет.

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

public void method1() {
    synchronized (Integer.class) {
        System.out.println("Aquired lock on Integer.class object");

        synchronized (String.class) {
            System.out.println("Aquired lock on String.class object");
        }
    }
}

public void method2() {
    synchronized (Integer.class) {
        System.out.println("Aquired lock on Integer.class object");

        synchronized (String.class) {
            System.out.println("Aquired lock on String.class object");
        }
    }
}

Теперь не будет тупиков, поскольку оба метода получают доступ к блокировке объекта Integer и объекта String в одном и том же порядке.таким образом, если поток A получает блокировку для объекта Integer, поток B не будет продолжаться до тех пор, пока поток A не освободит блокировку Integer, точно так же поток A не будет заблокирован, даже если поток B удерживает блокировку строки, поскольку теперь поток B не будет ожидать, что поток A освободит блокировку Integerчтобы продолжить.

Предоставлено

...