Тупиковые блокировки в Java: когда они возникают?

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

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

Спасибо!


Обновление

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

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

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

10
задан Albus Dumbledore 2 September 2010 в 18:38
поделиться

3 ответа

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

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

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

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

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

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

С внутренними блокировками Java (т.е. синхронизированными блоками) базовый объект Блокировка сам по себе не виден в коде, только объект, который мы фиксируем. Учтите, что

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 использует частный объект в качестве блокировки. Если этот объект каким-либо образом не публикуется внешним сторонам, никто другой не может (непреднамеренно или злонамеренно) вызвать взаимоблокировку, связанную с этой блокировкой.

12
ответ дан 3 December 2019 в 22:34
поделиться

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

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

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

2
ответ дан 3 December 2019 в 22:34
поделиться
1
ответ дан 3 December 2019 в 22:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: