Я добавил комментарии в код, чтобы объяснить, откуда происходит тупик. В принципе, есть две нити. Каждый поток получает блокировку на объекте Manager
, а затем переходит на получение блокировки статического ресурса, который представляет собой карту всех объектов Manager
в приложении. Оба потока набирают get()
на карте. Manager
класс переопределил equals()
способ. equals()
далее вызывает некоторый синхронизированный метод класса Manager
. Таким образом, get()
на карте потребуется блокировка уровня объекта для каждого объекта на карте по одному, пока ключ не совпадёт, потому что равны переопределены. Я могу изменить код только в подклассах (Sub1
и Sub2
) и избежать тупика, поскольку у меня нет доступа к другим классам.Как решить этот тупик?
Редактировать: У меня нет доступа к syncMap. Код в «синхронизированном» блоке выполняется в стороннем коде, API которого я вызываю.
Могу ли я избежать этого, установив замок в finally
на Manager
, а не перед тем, как попробовать блок?!
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();
}
}
}
}
Почему, по вашему мнению, будет тупик? этот код применяет упорядоченную внутреннюю блокировку с синхронизированным (диспетчером), поэтому я не вижу тупика ... – nukie
Я столкнулся с тупиком в моей организации-работодателе. И свалка говорит, что это происходит только здесь. Thread-1 имеет блокировку уровня на syncMap и ожидает блокировки экземпляра класса Manager, доступного в SyncMap. Другой поток уже блокирует объект Manager и ожидает блокировки на SyncMap. Менеджер может иметь много экземпляров, и все экземпляры находятся в SyncMap для записи. – user5415672
Нет никакого смысла, чтобы заставить дизайн, где 'equals' делает синхронизированный материал, избавиться от него. В лучшем случае оценка 'equals' в каком-либо клиентском коде скажет вам, что такое состояние * во время вызова equals * и не дает никаких обещаний состояния после вызова. Вместо этого выполняйте атомные операции ('trySomeOperation'), которые сообщают вызывающему, если требуемые условия были такими, как ожидалось, и операция была выполнена. – BeyelerStudios