Как получить блокировку ключом
Каков наилучший способ предотвратить одновременное обновление одной записи в наборе ключевых значений без блокировки всего набора? Семантически, я ищу некоторую блокировку ключом (в идеале, реализацией Java, но не обязательно):
interface LockByKey {
void lock(String key); // acquire an exclusive lock for a key
void unlock(String key); // release lock for a key
}
Эта блокировка предназначена для синхронизации доступа к удаленному хранилищу, поэтому некоторая синхронизированная коллекция Java не является вариантом.
Ответы
Ответ 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
.