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