代码之家  ›  专栏  ›  技术社区  ›  SSpoke

Java ConcurrentHashMap不是线程安全的。。什么?

  •  8
  • SSpoke  · 技术社区  · 14 年前

    我以前用过HashMap

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

    现在我已经切换到ConcurrentHashMap以避免同步块,现在我遇到的问题是,我的服务器每秒有200-400个并发客户机负载,这预计会随着时间的推移而增长。

    现在看起来像这样

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

    我的服务器设计是这样的。 我有一个用于处理大量数据包的工作线程。每个数据包都由packetHandler子例程(不是线程的一部分)进行检查几乎任何客户机都可以随时调用它—它几乎像静态的,但实际上不是。

    除了包处理部分,我的整个服务器大部分是单线程的。

    不管怎样,当有人使用像count up all clients online这样的命令时,从他们那里获取一些信息。

    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异常来修复它,以跳过这些空客户机。

    但我不明白,如果它检查以上如果(人!=null)嵌套的代码不应该自动工作。。

    如果这并不意味着它在迭代时被删除了,那么这应该是不可能的,因为它是线程安全的wtf?

    这是个例外

    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 回复  |  直到 14 年前
        1
  •  16
  •   Stephen C    14 年前

    您需要阅读 ConcurrentHashMap.values() 方法,特别注意这个描述迭代器对于 values() 收藏作品:

    视图的迭代器是一个“弱一致”迭代器,它永远不会抛出ConcurrentModificationException,并保证遍历迭代器构造时存在的元素,并且可能(但不保证)反映构造之后的任何修改

    迭代器不会提供值集合状态的一致快照,但是 它是线程安全的 ,预期的行为范围为 明确规定

        2
  •  3
  •   heekyu    14 年前

    java.util.concurrent文件.ConcurrentHashMap没有 允许空值。所以,空检查(人!=null)不需要。

        3
  •  1
  •   Steve Emmerson    14 年前

    我看不出你的代码有什么问题。因为车祸不太可能发生在 else ,很可能 getGender() null .

        4
  •  1
  •   James Black    14 年前

    您可能会发现,在对地图进行迭代时,无法对其进行修改。如果是这种情况,您可能希望在一个单独的集合中获取值和键,并对其进行迭代,因为它是不可变的。

    它并不完美,但另一个选择是扩展 ConcurrentHashMap 当添加或删除某些内容时,您将更新这四个变量,这样您就不必每次都遍历整个列表,因为这似乎是在浪费cpu周期。

    这篇文章讨论了这样一个事实,即改进的并发性是因为放松了一些承诺。 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