WedX - журнал о программировании и компьютерных науках

ConcurrentHashMap и составные операции

Hashtable и Collections.synchronizedMap являются потокобезопасными, но все же составными операциями, такими как

if (!map_obj.containsKey(key)) {
   map_obj.put(key, value);
}

требуется внешняя синхронизация как:

synchronized(map_obj) {
    if (!map_obj.containsKey(key)) {
       map_obj.put(key, value);
    }
}

Предположим, у нас есть ConcurrentHashMap(CHM) вместо Hashtable или HashMap. CHM предоставляет альтернативный метод putIfAbsent() для вышеуказанной составной операции, что устраняет необходимость во внешней синхронизации.

Но предположим, что CHM не предоставляет putIfAbsent(). Тогда мы можем написать следующий код:

synchronized(concurrenthashmap_obj) {
    if (!concurrenthashmap_obj.containsKey(key)) {
       concurrenthashmap_obj.put(key, value);
    }
}

Я имею в виду, можем ли мы использовать внешнюю синхронизацию на объекте CHM? Будет ли это работать?

Для вышеописанной составной операции в CHM есть метод putIfAbsent(), но как мы можем добиться безопасности потоков для других составных операций, если мы используем CHM. Я имею в виду, можем ли мы использовать внешнюю синхронизацию на объекте CHM?


Ответы:


1

Нет, вы не можете использовать внешнюю синхронизацию для обеспечения атомарности составных операций над ConcurrentHashMap.

Чтобы быть точным, вы можете использовать внешнюю синхронизацию для обеспечения атомарности составных операций, но только если все операции с ConcurrentHashMap также синхронизируются через одну и ту же блокировку (хотя использование ConcurrentHashMap в этом случае не имеет смысла — вы можете заменить его на обычный HashMap).

Подход с внешней синхронизацией работает с Hashtable и Collections.synchronizedMap() только потому, что они гарантируют, что их примитивные операции будут synchronized и над этими объектами. Поскольку ConcurrentHashMap не дает такой гарантии, примитивные операции могут мешать выполнению ваших составных операций, нарушая их атомарность.

Однако ConcurrentHashMap предоставляет ряд методов, которые можно использовать для реализации составных операций оптимистичным образом:

  • putIfAbsent(key, value)
  • remove(key, value)
  • replace(key, value)
  • replace(key, oldValue, newValue)

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

09.02.2014
  • ваш ответ противоречит другим ответам. 09.02.2014
  • Но что делать с другими составными операциями, которым нет замены, например методу putIfAbsent(). 09.02.2014
  • Обновлено. Вам придется синхронизировать все операции над ConcurrentHashMap, чтобы все заработало. 09.02.2014
  • Благодарим за рассмотрение условия, когда другой поток пытается выполнить базовую конфликтующую операцию на карте. 09.02.2014
  • @aLearner Краткая версия: если вы синхронизируетесь с ConcurrentHashMap, вы делаете это неправильно. Весь смысл этого класса в том, что вам не нужно синхронизироваться, чтобы использовать его; вы используете другой набор операций, который позволяет избежать таких затрат. 10.02.2014

  • 2

    Нет никаких причин, почему вы не можете. Традиционная синхронизация работает со всем, особых исключений для них нет. ConcurrentHashMaps просто использует более оптимизированные механизмы потокобезопасности, если вы хотите сделать что-то более сложное, возврат к традиционной синхронизации может быть вашим единственным вариантом (это и использование блокировок).

    09.02.2014

    3

    Вы всегда можете использовать блок synchronized. Причудливые коллекции в java.util.concurrent не запрещают этого, они просто делают его избыточным для наиболее распространенных случаев использования. Если вы выполняете составную операцию (например, вы хотите вставить два ключа, которые всегда должны иметь одинаковое значение), вы не только можете использовать внешнюю синхронизацию, но и должны.

    E.g.:

    String key1 = getKeyFromSomewhere();
    String key2 = getKeyFromSomewhereElse();
    String value = getValue();
    
    // We want to put two pairs in the map - [key1, value] and [key2, value]
    // and be sure that in any point in time both key1 and key2 have the same 
    // value
    synchronized(concurrenthashmap_obj) {
        concurrenthashmap_obj.put(key1, value);
    
        // without external syncronoziation, key1's value may have already been
        // overwritten from a different thread!
        concurrenthashmap_obj.put(key2, value);
    }
    
    09.02.2014
  • ответ от axtavt противоречит вашему ответу. Я хочу услышать ваше мнение об ответе, данном axtavt. 09.02.2014

  • 4

    Поскольку ConcurrentHashMap реализует интерфейс карты, он поддерживает все функции, которые есть у каждой базовой карты. Так что да: вы можете использовать ее как любую другую карту и игнорировать все дополнительные функции. Но тогда у вас будет более медленный HashMap.

    Основное различие между синхронизированной картой и параллельной картой, как следует из названия, заключается в параллелизме. Представьте, что у вас есть 100 потоков, желающих читать с карты, если вы synchronize заблокируете 99 потоков, а 1 сможет выполнить работу. Если вы используете параллелизм, 100 потоков могут работать одновременно.

    Теперь, если вы подумаете о реальной причине использования потоков, вы вскоре придете к выводу, что вам следует избавиться от всех возможных блоков synchronized, какие только можно.

    09.02.2014

    5

    Все зависит от того, что вы подразумеваете под «другой сложной операцией» и под «работой». Синхронизация работает с ConcurrentHashMap точно так же, как и с любым другим объектом.

    Таким образом, если вы хотите, чтобы какое-то сложное изменение общего состояния рассматривалось как атомарное изменение, то все обращения к этому общему состоянию должны быть синхронизированы с одной и той же блокировкой. Этот замок может быть самой картой или другим объектом.

    09.02.2014

    6

    О java.util.concurrent.ConcurrentHashMap

    • «полностью совместим с Hashtable в программах, которые полагаются на его потокобезопасность, но не на детали синхронизации: они не вызывают исключение ConcurrentModificationException».

    • "разрешает параллелизм между операциями update"

    О java-памяти

    Вообще говоря, чтение безопасно с точки зрения синхронизации, но не с точки зрения памяти.

    См. также "https://www.ibm.com/developerworks/java/library/j-jtp03304/".

    Таким образом, synchronizaton и volatile следует использовать для управления параллельным чтением (по сравнению с записью).

    О putIfAbsent

    putIfAbsent – это ваш друг:

    Если указанный ключ еще не связан со значением, свяжите его с данным

    value. This is equivalent to
       if (!map.containsKey(key))
           return map.put(key, value);
       else
           return map.get(key);
    

    за исключением того, что действие выполняется !!!атомарно!!!.

    09.02.2014
    Новые материалы

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..

    ИИ в аэрокосмической отрасли
    Каждый полет – это шаг вперед к великой мечте. Чтобы это происходило в их собственном темпе, необходима команда астронавтов для погони за космосом и команда технического обслуживания..


    Для любых предложений по сайту: [email protected]