Подтвердить что ты не робот

Как получить блокировку ключом

Каков наилучший способ предотвратить одновременное обновление одной записи в наборе ключевых значений без блокировки всего набора? Семантически, я ищу некоторую блокировку ключом (в идеале, реализацией Java, но не обязательно):

interface LockByKey {
   void lock(String key); // acquire an exclusive lock for a key   
   void unlock(String key); // release lock for a key
}

Эта блокировка предназначена для синхронизации доступа к удаленному хранилищу, поэтому некоторая синхронизированная коллекция Java не является вариантом.

4b9b3361

Ответ 1

У Guava есть что-то подобное, выпущенное в 13.0; вы можете получить его из HEAD, если хотите.

Striped<Lock> более или менее выделяет определенное количество блокировок, а затем назначает строки блокировкам на основе их хэш-кода. API выглядит примерно так:

Striped<Lock> locks = Striped.lock(stripes);
Lock l = locks.get(string);
l.lock();
try {
  // do stuff 
} finally {
  l.unlock();
}

Более или менее контролируемое количество полос позволяет вам торговать concurrency с использованием памяти, поскольку распределение полной блокировки для каждого строкового ключа может стать дорогостоящим; по сути, вы получаете только блокировку, когда получаете хеш-коллизии, которые (как предсказуемо) редки.

(Раскрытие: я вношу свой вклад в Гуаву.)

Ответ 2

Вот как; я сделал это. И да, я согласен, что если две разные строки разделяют один и тот же хэш-код, он получит тот же самый замок.

class LockByKey {
    ObjectForString objHolder = new ObjectForString(100);
    public void lockThenWorkForKey (String key) {
        synchronized(objHolder.valueOf(key)){
            //DoSomeWork
        }
    }
}

public final class ObjectForString {

    private final Object[] cache;
    private final int cacheSize;
    final int mask;

    public ObjectForString(int size) {
        // Find power-of-two sizes best matching arguments
        int ssize = 1;
        while (ssize < size) {
            ssize <<= 1;
        }

        mask = ssize - 1;
        cache = new Object[ssize];
        cacheSize = ssize;
        //build the Cache
        for (int i = 0; i < cacheSize; i++) {
            this.cache[i] = new Object();
        }
    }

    public Object valueOf(String key) {
        int index = key.hashCode();
        return cache[index & mask];
    }
}

Ответ 3

Хранить мьютексы/блокировки на каждый ковш. Это гарантирует, что на этом мьютексе ждут только столкновения.

Ответ 4

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

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

В любом случае вы должны смотреть на классы в пакете java.util.concurrent.