Java: одновременные коллекции - PullRequest
3 голосов
/ 09 февраля 2012

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

/**
 * Acts as a broker for a concurrent hash map that stores its keys in order 
 * of submission. At shipping time, the concurrent map is "sealed" 
 * (picture a truck with its cargo door being closed)
 * and its contents presented as an immutable map, and is replaced 
 * by a new concurrent map ready to accept values.
 *
 * Consumers of this class that submit information to it, are expected to 
 * know that this contains a concurrent collection, and should use the 
 * compareAndSet paradigm, e.g. the following:
 *
 * LoadingDock loadingDock = ...
 * boolean done = false;
 * while (!done)
 * {
 *    V oldValue = loadingDock.get();
 *    V newValue = computeNewValue(oldValue, otherInformation);
 *    if (oldValue == null)
 *       done = loadingDock.putIfAbsent(newValue) == null;
 *    else
 *       done = loadingDock.replace(oldValue, newValue) == oldValue;
 * }
 *    
 *
 * Keys and values must be non-null. Keys are not ordered.
 */
class LoadingDock<K,V>
{
    /**
     * analogous to ConcurrentMap's replace, putIfAbsent, and get methods
     */
    public boolean replace(K key, V oldValue, V newValue);
    public V putIfAbsent(K key, V value);
    public V get(K key)

    /* see above */
    public Map<K,V> ship();
}

У меня есть две проблемы с этим.

Во-первых, ни Java, ни Guava не содержат ConcurrentLinkedHashMap. Это заставляет меня задуматься, почему нет - может быть, я скучаю по тонкостям такого зверя. Похоже, я мог бы сделать это сам, украсив ConcurrentHashMap классом, который добавляет ключ к списку, если putIfAbsent() когда-либо вызывается и возвращает ноль - мне не нужны какие-либо другие методы в ConcurrentHashMap, кроме тех, что выше, поэтому нет никакого способа добавить новый ключ на карту, кроме как через вызов putIfAbsent().

Другая, более коварная проблема заключается в том, что я не могу думать о том, как реализовать ship() без блокировки синхронизации - когда вызывается ship (), LoadingDock должен перенаправлять все новые вызовы на новую карту. и не может вернуть старую карту, пока не будет уверена, что все одновременные записи выполнены. (В противном случае я бы просто использовал AtomicReference для хранения параллельной карты.)

Есть ли способ сделать это без синхронизации?

1 Ответ

5 голосов
/ 09 февраля 2012

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

Для метода ship () просто используйте ReadWriteLock , который имеетчестный режим включен.Потоки, которые хотят добавить на карту, приобретают блокировку чтения (странная семантика, которую я знаю, но это то, как она работает, думайте о ней как о режиме чтения для фактической ссылки на карту, которая затем обычно используется), так что столько, сколькохочу можно добавлять одновременно.В методе корабля вы получаете блокировку записи, и она блокирует изменение карты во время экспорта и создания новой карты.Fair-mode делает так, чтобы вы «обрезали» были сумматоры как можно ближе к тому, когда вызывается ship (), и просто позволяете существующим завершиться.

...