8

我之前使用过 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 异常来跳过这些空客户端来修复它。

但是我不明白如果它在上面检查 if(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

4 回答 4

16

您需要阅读该ConcurrentHashMap.values()方法的 javadocs,特别注意对values()集合的迭代器如何工作的描述:

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

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

如果您想要一个 Map 实现,它为您提供映射中值(或键或条目)的一致快照,并允许您与修改同时迭代,您可能需要创建一个自定义 Map 包装类(复制集合atomically) ... 或一个成熟的自定义 Map 实现。对于您的用例,两者都可能比 ConcurrentHashMap 慢很多。

于 2010-09-16T01:12:07.827 回答
3

java.util.concurrent.ConcurrentHashMap不允许空值。因此,您的代码中的 null check(person != null) 是不必要的。

如果要在迭代时拒绝修改 Map,则必须在上述代码和所有修改操作代码中使用同步块。

于 2010-09-16T01:21:42.410 回答
1

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

它并不完美,但另一种选择是扩展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

于 2010-09-16T01:11:23.093 回答
1

我看不出你的代码有什么问题。因为崩溃不太可能实际发生在else,所以该getGender()方法很可能正在返回null

于 2010-09-16T01:16:01.427 回答