Как избежать вложенной синхронизации и возникшей тупиковой ситуации - PullRequest
2 голосов
/ 01 марта 2011

Мне нужно заблокировать два объекта в функциональности, и текущий код выглядит следующим образом:

Object obj1  = ...//get from somewhere
Object obj2 = ...//get from somewhere

synchronized(obj1){
  ...//blah
  synchronized(obj2){
     ...//blah
  }
}

Как видите, это простой и прямой рецепт взаимоблокировок, если другой поток запускает этот кусок кода с obj1 и двумя обратными.
Есть ли способ избежать этой ситуации, используя блокировки параллелизма?

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

Ответы [ 4 ]

6 голосов
/ 01 марта 2011

Хотя вы сохраняете порядок блокировки, если obj1 переключается с obj2, вы попадаете в тупик.

Вы должны искать другое решение, чтобы избежать этого: упорядочение блокировки + дополнительная блокировка разрыва связи

int fromHash = System.identityHashCode(obj1);
int toHash = System.identityHashCode(obj2);

if (fromHash < toHash) {
    synchronized (obj1) {
        synchronized (obj2) {
               ........
        }
    }
} else if (fromHash > toHash) {
    synchronized (obj2) {
        synchronized (obj1) {
            ........
        }
    }
} else {
    synchronized (TIE_LOCK) {
        synchronized (fromAcct) {
            synchronized (toAcct) {
               ...
            }
        }
    }
3 голосов
/ 01 марта 2011

В зависимости от того, что вы делаете, вы можете взять то, что вы хотите, от первого заблокированного объекта и использовать эту информацию для обработки второго заблокированного объекта. например,

вместо

synchronized(list1) {
  for(String s : list1) {
     synchronized(list2) {
       // do something with both lists.
     }
  }
}

сделать это

List<String> listCopy;
synchronized(list1) {
  listCopy = new ArrayList<String>(list1);
}

synchornized(list2) {
   // do something with liastCopy and list2
}

Вы можете видеть, что у вас есть только блокировка одновременно, поэтому вы не получите тупик.

2 голосов
/ 01 марта 2011

Вам необходимо последовательно заблокировать в порядке obj1, а затем obj2.Если вы никогда не нарушите этот порядок, у вас не будет тупиков.

1 голос
/ 15 января 2015

По сути, у вас есть проблема обедающего философа.

https://en.wikipedia.org/wiki/Dining_philosophers_problem

Ответ Овидиу Лупаса аналогичен решению Dijkstra Resource Heirarchy, но есть еще 3 решения, объясненные на вики-странице

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

import java.util.concurrent.locks.Lock;

public void init()
{
  Lock arbitrator = new Lock();
}

public void meth1()
{
  arbitrator.lock();
  synchronized (obj1) {
    synchronized (obj2) {
      arbitrator.unlock();
      // Do Stuff
    }
  }
}

public void meth2()
{
  arbitrator.lock();
  synchronized (obj2) {
    synchronized (obj1) {
      arbitrator.unlock();
      // Do Stuff
    }
  }
}

Решение Ченди / Мисры требует многопередачи сообщений, поэтому я не собираюсь это реализовывать, но в википедии есть довольно хорошее объяснение

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...