Java ConcurrentHashMap не является потокобезопасным .. wth? - PullRequest
8 голосов
/ 16 сентября 2010

Раньше я использовал 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, чтобы пропустить эти нулевые клиенты.

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

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

Что я должен делать?или try-catch 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 содержит приведенный выше код.и комментарий указывает количество строк #

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

Ответы [ 4 ]

16 голосов
/ 16 сентября 2010

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

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

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

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

3 голосов
/ 16 сентября 2010

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

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

1 голос
/ 16 сентября 2010

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

1 голос
/ 16 сентября 2010

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

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

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

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

объяснение свойств согласованности памяти: http://download -llnw.oracle.com / JavaSE / 6 / документы / API / Java / Util / параллельный / пакет-summary.html # MemoryVisibility

...