Я добавил комментарии в код, чтобы объяснить, откуда возникает взаимоблокировка. По сути, есть две нити. Каждый поток получает блокировку объекта Manager
, а затем переходит к получению блокировки статического ресурса, который представляет собой карту всех объектов Manager
в приложении. Оба потока вызывают get()
на карте. Класс Manager
переопределил метод equals()
. equals()
далее вызывает некоторый синхронизированный метод класса Manager
. Таким образом, get()
на карте потребуется блокировка уровня объекта для каждого объекта на карте один за другим, пока ключ не совпадет, потому что равенство переопределяется. Я могу изменить код только в подклассах (Sub1
и Sub2
) и избежать взаимоблокировки, так как у меня нет доступа к другим классам.
Редактировать: у меня нет доступа к syncMap. Код в «синхронизированном» блоке выполняется в стороннем коде, API которого я вызываю.
Могу ли я избежать этого, установив блокировку в finally
на Manager
, а не перед блоком try ?!
public class Parent{
protected Manager manager;
}
public class Global{
private static final Map syncMap = Collections.synchronizedMap(new HashMap());
//syncMap contains all the objects of Manager in the application
}
class Manager{
public boolean equals(Object o){
Manager obj = (Manager)o;
return obj.getURL().equals(getURL());
}
public final synchronized String getURL(){
return msettings.getDBURL(); //msettings is a global variable
}
}
//Thread-1 is executing someMethod() of this class
class Sub1 extends Parent{
Global global;
//consider manager and Global object are not null
public void someMethod()
{
synchronized(manager){// Thread-1 succesfully takes object level lock on a manager object, say Manager01
try{
global.syncMap.get(manager);
// Thread-1 Succesfully takes class level lock on syncMap
// get() calls equals() for each object in syncMap.
//equals() need object lock on each Manager Object in map as it further calls synchronized getURL()
// But on one manager Object(Manager02) Thread-2 has already acquired lock and is waiting for lock on syncMap which this thread-1 holds
}
finally{
manager.releaseConnection();
}
}
}
}
//Thread-2 is executing otherMethod() of this class
class Sub2 extends Parent{
public void otherMethod()
{
synchronized(manager){// this takes a lock on manager(Manager02)
try{
global.syncMap.get(manager);
// this is blocked as syncMap is aquired by thread-1
}
finally{
manager.releaseConnection();
}
}
}
}
equals
выполняет синхронизированные действия. В лучшем случае оценкаequals
в каком-либо клиентском коде скажет вам, какое состояние было во время вызова equals, и не обещает состояние после вызова. Вместо этого реализуйте атомарные операции (trySomeOperation
), которые сообщают вызывающему объекту, были ли требуемые условия такими, как ожидалось, и операция была выполнена. - person BeyelerStudios   schedule 18.03.2016