Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Java ConcurrentHashMap не является потокобезопасным .. wth?

Автор вопроса: SSpokeОтветов: 4

Раньше я использовал HashMap, например

   public Map<SocketChannel, UserProfile> clients = new HashMap<SocketChannel, UserProfile>();

теперь я переключился на ConcurrentHashMap, чтобы избежать синхронизированных блоков, и теперь у меня проблемы, мой сервер сильно загружен 200-400 одновременными клиентами каждую секунду, и ожидается, что со временем это число будет расти.

который теперь выглядит так

public ConcurrentHashMap<SocketChannel, UserProfile> clients = new ConcurrentHashMap<SocketChannel, UserProfile>();

Мой дизайн сервера работает так. У меня есть рабочий поток(и) для обработки огромного количества пакетов. Каждый пакет проверяется с помощью подпрограммы packetHandler (не являющейся частью потока), практически любой клиент может вызвать ее в любое время, она почти статична, но это не так.

Весь мой сервер в основном однопоточный, за исключением части обработки пакетов.

В любом случае, когда кто-то использует команду, например, подсчитывает всех клиентов онлайн и получает от них некоторую информацию.

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

Также я хотел бы добавить сюда код.

                int txtGirls=0;
                int vidGirls=0;
                int txtBoys=0;
                int vidBoys=0;
                Iterator i = clients.values().iterator();
                while (i.hasNext()) {
                    UserProfile person = (UserProfile)i.next();
                    if(person != null) {
                        if(person.getChatType()) {
                            if(person.getGender().equals("m"))
                                vidBoys++;
                            else //<-- crash occurs here.
                                vidGirls++;
                        } else if(!person.getChatType()) {
                            if(person.getGender().equals("m"))
                                txtBoys++;
                            else
                                txtGirls++;
                        }
                    }
                }

Я имею в виду, конечно, что я собираюсь исправить это, добавив исключение try-catch внутри Iterator, чтобы пропустить эти нулевые клиенты.

Но чего я не понимаю, если он проверяет выше, if(person != null) не должен ли вложенный код работать автоматически..

если это не означает, что он был удален во время итерации, что должно быть невозможно, поскольку он потокобезопасен, wtf?

Что я должен делать? или лучше всего попробовать Exception?

Вот исключение

java.lang.NullPointerException
    at Server.processPackets(Server.java:398)
    at PacketWorker.run(PacketWorker.java:43)
    at java.lang.Thread.run(Thread.java:636)

ProcessPackets содержит приведенный выше код. а в комментарии указано количество строк #

Спасибо, что просветили меня.

  • Вы пробовали Collections.synchronizedMap(map) ? zengr 16.09.2010
  • TF заключается в том, что ConcurrentHashMap является потокобезопасным, но помимо потокобезопасности вы ожидаете чего-то еще. Stephen C 16.09.2010
  • также было бы полезно, если бы вы объяснили, что здесь означает сбой. Что за авария? Что за исключение? Stephen C 16.09.2010
  • Пожалуйста, ответьте на вопросы @Stephen C и перепишите это как вопрос, а не как историю. Jacob Tomaw 16.09.2010
  • Извините, я новичок во всей этой сделке, но я отредактировал вопрос и поставил ошибку исключения, но это все еще очень туманная трассировка стека. Что бы вы, ребята, порекомендовали вместо расширения класса что-то простое. Я бы сделал идею копирования коллекции, но я не уверен, что это вообще возможно быть на 100% безопасным, поскольку все объекты связаны ссылками, так что в любом случае это бессмысленно, если скопированный объект деконструируется/уничтожается где-то еще? SSpoke 16.09.2010
  • JAVA DOC (docs.oracle.com/javase /6/docs/api/) говорит, что класс ConcurrentHashMap полностью совместим с Hashtable в программах, которые полагаются на его безопасность потоков, но не на его детали синхронизации. Kanagavelu Sugumar 25.10.2013

javatry-catchnullpointerexceptionnullconcurrenthashmap

Ответы

Ответ 1 · рейтинг 16 · Stephen C

Вам необходимо прочитать javadocs для ConcurrentHashMap.values(), обратив особое внимание на это описание того, как работает итератор для коллекции values():

"Итератор представления - это "слабо согласованный" итератор, который никогда не будет генерировать исключение ConcurrentModificationException и гарантирует обход элементов в том виде, в каком они существовали при построении итератора, и может (но не обязательно) отражать любые модификации после построения".

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

Если вам нужна реализация Map, которая дает вам непротиворечивый снимок значений (или ключей, или записей) в карте И позволяет выполнять итерацию одновременно с изменениями, вам, вероятно, потребуется создать собственный класс-оболочку Map (который копирует коллекции атомарно) ... или полномасштабная пользовательская реализация карты. Оба, вероятно, будут намного медленнее, чем ConcurrentHashMap для вашего варианта использования.

Ответ 2 · рейтинг 3 · heekyu

java.util.concurrent.ConcurrentHashMap не допускает нулевое значение. Итак, нулевая проверка (человек != нуль) в вашем коде не нужна.

Если вы хотите запретить изменение карты во время итерации, вы должны использовать блок синхронизации в приведенном выше коде и все коды операций модификации.

Ответ 3 · рейтинг 1 · Steve Emmerson

Я не вижу ничего плохого в вашем коде. Поскольку маловероятно, что сбой на самом деле происходит в else, вполне вероятно, что метод getGender() возвращает null.

Ответ 4 · рейтинг 1 · James Black

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

Это не будет идеальным, но другой вариант - расширить ConcurrentHashMap, и когда что-то добавляется или удаляется, вы обновляете эти четыре переменные, поэтому вам не нужно каждый раз перебирать весь список, так как это кажется пустой тратой времени. циклов процессора.

Вот пара ссылок, которые могут быть полезны:

Здесь немного говорится о том, что улучшенный параллелизм связан с ослаблением некоторых промисов. http://www.ibm.com/developerworks/java/library/j-jtp07233.html

объяснение свойств согласованности памяти: http://download-llnw.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы