Иерархические блокировки мьютекса в Java - PullRequest
11 голосов
/ 16 февраля 2012

Я хочу иметь возможность блокировки на основе иерархии файловой системы. Например:

Тема 1:

lock("/");
doStuff();
unlock();

Тема 2:

lock("/sub/foo");
doStuff();
unlock();

Тема 3:

lock("/sub/bar");
doStuff();
unlock();

Если поток 1 сначала получает блокировку, то потоки 2 и 3 будут заблокированы, пока поток 1 не разблокируется. Однако, если поток 2 сначала получает блокировку, тогда поток 3 должен быть в состоянии выполняться одновременно с потоком 2. Общее правило состоит в том, что если есть блокировка в родительском каталоге, то поток должен блокировать.

Есть ли в Java что-нибудь встроенное, что может помочь решить эту проблему? Я хочу избежать хранения блокировки для каждого каталога, потому что там будут сотни тысяч каталогов.

Ответы [ 2 ]

7 голосов
/ 16 февраля 2012

Я бы сохранил пути к каталогам в дереве следующим образом:

- /
 - sub
  - foo
  - bar

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

Эта схема гарантирует вам тупиковую свободу и стабильность соответствующих частей дерева.

Я не вижу конкретной проблемы при хранении сотен тысячзамки.Это, вероятно, приведет к потере, возможно, 100 байт ОЗУ на блокировку.Но это упрощает архитектуру.Вы измерили, если это действительно проблема?

В качестве альтернативы вы можете иметь карту от пути к замку.Все операции в этом словаре должны синхронизироваться звонящими.Это позволяет лениво инициализировать блокировки.Вы также можете периодически собирать мусор неиспользуемыми блокировками, сначала взяв блокировку записи в корне, которая прекращает все операции.Как только все станет тихо, вы отбросите все не-root блокировки.

0 голосов
/ 16 февраля 2012

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

Я бы создал общий объект TreeAccess с lock(path) и unlock(path) методом.Этот метод должен был бы ввести синхронизированный блок, который будет зацикливаться, пока путь не будет доступен.На каждой итерации, если она недоступна, она будет проверять, доступен ли путь, а если нет, wait(), пока какой-то другой поток не вызовет notifyAll().Если путь доступен, он будет продолжен, и когда он будет завершен, вызовите метод unlock (), который будет notifyAll().

. Прежде чем продолжить, необходимо сохранить заблокированный путь в некоторой структуре данных.И перед уведомлением вы должны удалить разблокированный путь из этой структуры данных.Чтобы проверить, доступен ли путь, вам необходимо выяснить, существует ли какой-либо путь в этой структуре данных, который равен или является предком пути, который вы хотите заблокировать.

public void lock(String path) {
    synchronized (lock) {
        while (!available(path)) {
            lock.wait();
        }
        lockedPaths.add(path);
    }
}

public void unlock(String path) {
    synchronized (lock) {
        lockedPaths.remove(path);
        lock.notifAll();
    }
}

private boolean available(String path) {
    for (String lockedPath : lockedPaths) {
        if (isParentOrEqual(lockedPath, path) { // this method is left as an exercise
            return false;
        }
    }
    return true;
}
...