Использование MapConstraint в guava на как-то синхронизированной карте - PullRequest
2 голосов
/ 07 февраля 2011

Используя Гуава, я хотел бы иметь карту со следующими свойствами:

  • Много читает, но очень мало пишет.
  • срок действия данных не истек.
  • должен быть синхронизирован, поэтому запись является «атомарной», и многократные чтения не мешают друг другу.
  • карта должна использовать MapConstraint API, и некоторые из них MapConstraint соответствуют содержимому самой карты (обычно, если существуют записи или другая, не перезаписывайте ее: вместо этого выведите IllegalStateException). Я вижу, что интерфейс MapConstraint не дает ограничения на Map.
  • проверка MapConstraint должна выполняться внутри части синхронизации.

Я хорошо подумал об использовании ReadWriteLock, но мне интересно, может ли MapMaker помочь мне здесь, так как я не очень знаком с этим API.

Так, каковы мои варианты?


Редактировать: Моя цель не простая putIfAbsent: мне нужно выполнить несколько проверок с картой перед вставкой значения, всегда в синхронизированной записи.

Ответы [ 2 ]

1 голос
/ 07 февраля 2011

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

MapConstraints.constrainedMap(map, new MyCustomMapConstraint(map));

Но это было бы ужасно / рискованно. Кто-то мог ошибочно сделать:

MapConstraint constraint = new MyCustomMapConstraint(firstMap);
Map constrainedMap = MapConstraints.constrainedMap(secondMap, constraint);

Кроме того, это не решит проблему синхронизации.


Если бы я это сделал, я бы использовал метод putIfAbsent, предоставленный ConcurrentMap. Я хотел бы создать оболочку ConcurrentMap, используя ForwardingConcurrentMap:

public class ProtectionistMap<K, V> extends ForwardingConcurrentMap<K, V> {

    private final ConcurrentMap<K, V> delegate;

    public ProtectionistMap(ConcurrentMap<K, V> delegate) {
        this.delegate = checkNotNull(delegate);
    }

    @Override
    protected ConcurrentMap<K, V> delegate() {
        return delegate;
    }

    @Override
    public V put(K key, V value) {
        V result = putIfAbsent(key, value);

        // The second part of the test is necessary when a map may contain null values...
        if (result != null || value == null && containsKey(key)) {
            throw new IllegalArgumentException("Map already had an entry for key " + key);
        }
        return result;
    }

    @Override
    public void putAll(Map<? extends K, ? extends V> map) {
        standardPutAll(map);
    }
}
0 голосов
/ 07 февраля 2011

Мой ответ совсем не о Гуаве, но почему бы не рассмотреть использование java.util.concurrent.ConcurrentHashMap?В нем есть все, что вам нужно, кроме прямой поддержки MapConstraint.

Если записи существуют, вы можете просто использовать метод containsKey () вместо исключения.Или, если вам нужно исключение, просто создайте свой собственный класс, унаследованный от ConcurrentHashMap.Он также может использовать MapConstraint, если вам это действительно нужно.Выглядит как 2-минутные задания, код будет выглядеть так:

public V put(K key, V value) {
    if (value == null)
        throw new NullPointerException();
    int hash = hash(key.hashCode());
    Segment seg = segmentFor(hash);
    V result;
    seg.lock()
    try {
        constraint.checkKeyValue(key, value);
        v = seg.put(key, hash, value, false)
    } finally {
        seg.unlock()
    }
    return result;
}

Не воспринимайте этот код буквально, это просто пример;)

Если вам нужна дополнительная информация о внутренностях ConcurrentHashMapВы можете посмотреть здесь: Раскрыта ConcurrentHashMap

...