2016-03-18 2 views
2

Я добавил комментарии в код, чтобы объяснить, откуда происходит тупик. В принципе, есть две нити. Каждый поток получает блокировку на объекте 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(); 
       } 

      } 
     } 
    } 
+0

Почему, по вашему мнению, будет тупик? этот код применяет упорядоченную внутреннюю блокировку с синхронизированным (диспетчером), поэтому я не вижу тупика ... – nukie

+0

Я столкнулся с тупиком в моей организации-работодателе. И свалка говорит, что это происходит только здесь. Thread-1 имеет блокировку уровня на syncMap и ожидает блокировки экземпляра класса Manager, доступного в SyncMap. Другой поток уже блокирует объект Manager и ожидает блокировки на SyncMap. Менеджер может иметь много экземпляров, и все экземпляры находятся в SyncMap для записи. – user5415672

+0

Нет никакого смысла, чтобы заставить дизайн, где 'equals' делает синхронизированный материал, избавиться от него. В лучшем случае оценка 'equals' в каком-либо клиентском коде скажет вам, что такое состояние * во время вызова equals * и не дает никаких обещаний состояния после вызова. Вместо этого выполняйте атомные операции ('trySomeOperation'), которые сообщают вызывающему, если требуемые условия были такими, как ожидалось, и операция была выполнена. – BeyelerStudios

ответ

1

Прежде всего, вы действительно должны попытаться устранить необходимость синхронизации в методе equals. Это вызовет больше проблем, чем решает, поэтому, если редизайн возможен, я думаю, что это лучший способ.

Однако, если вы реструктурировать код немного, и переместить global.syncMap.get(manager) к перед synchronization блока не будет генерировать тупиковый

public Class Parent{ 
    protected Manager manager; 
} 

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() 
    { 
     try { 
      global.syncMap.get(manager); 
      synchronized(manager){ 

      } 
     } 
     finally{ 
      manager.releaseConnection(); 
     }    
    } 
} 

//Thread-2 is executing otherMethod() of this class 
class Sub2 extends Parent{ 
    public void otherMethod() 
    { 
     try { 
      global.syncMap.get(manager); 
      synchronized(manager){ 

      } 
     } 
     finally{ 
      manager.releaseConnection(); 
     }       
    } 
} 

UPDATE Альтернативная синхронизация через Global.class, вероятно, может использовать переменную экземпляра global также вместо Global.class

ОБНОВЛЕНИЕ Измененная синхронизация должна быть более Manager.class i от Global.class.

class Sub1 extends Parent 
{ 
    Global global; 

    public void someMethod() 
    { 
     synchronized (Manager.class) { 
      try { 
       global.syncMap.get(manager); 
      } 
      finally { 
       manager.releaseConnection(); 
      } 
     } 
    } 
} 


class Sub2 extends Parent 
{ 
    Global global; 

    public void otherMethod() 
    { 
     synchronized (Manager.class) { 
      try { 
       global.syncMap.get(manager); 
      } 
      finally { 
       manager.releaseConnection(); 
      } 
     } 
    } 
} 
+0

, так что вы позволяете двум потокам работать с одними и теми же данными менеджера, поэтому могут возникать новые проблемы с параллелизмом: поскольку я вижу, что оригинальная идея заключалась в том, чтобы поместить вызов метода manager.releaseConnection() внутри синхронизированного блока ... – nukie

+0

Спасибо. Я не могу изменить дизайн. У меня есть доступ только к Sub-классам. И фактический синхронизированный блок похож на ниже, что делает вызов API. Внутри этого API все происходит плохо. Я извлек код из API и поставил синхронизированный блок, задавая вопрос для простоты. synchronized (manager) { try { fdb1Objssert = routedDrug.getFDBClassifications(); } наконец { manager.returnConnection(); } – user5415672

+0

Хорошо, я сделал обновление до ответа – gustf

1

После новой части информации я не вижу другого решения, кроме превращения всей обработки в последовательный. Таким образом, вы можете поместить все вызовы API, связанные с менеджером, в один синхронизированный метод некоторого класса упаковки и использовать эту оболочку в качестве единственной точки входа для стороннего API.

class BrutalWrapper { 
    public synchronized void doIt(Manager manager) 
    { 
     try{ 
      global.syncMap.get(manager); 

     } 
     finally{ 
      manager.releaseConnection(); 
     } 
    } 
} 

class Sub1 extends Parent{ 
    BrutalWrapper brutal; 
    public void someMethod() 
    { 
     brutal.doIt(manager); 
    } 
} 

class Sub2 extends Parent{ 
    BrutalWrapper brutal; 
    public void someMethod() 
    { 
     brutal.doIt(manager); 
    } 
} 
+0

У меня нет доступа к syncMap. Код, который я разместил в блоке синхронизации, выполняется в вызове API, к которому у меня нет доступа. У меня есть доступ только к объекту менеджера – user5415672

+0

Я отредактировал детали, чтобы включить это. – user5415672

+0

вы сказали, что можете изменить Sub1 и Sub2 класс, и вы показали объект Global как часть класса Sub1, а метод метода global.syncMap.get в Sub1 ... вы должны быть более конкретным в своем вопросе, затем – nukie

Смежные вопросы