Java ConcurrentHashMap 不是线程安全的..什么?
我以前使用过 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 包含上面的代码。评论指出了行数 #
感谢您启发我。
I was using HashMap before like
public Map<SocketChannel, UserProfile> clients = new HashMap<SocketChannel, UserProfile>();
now I've switched to ConcurrentHashMap to avoid synchronized blocks and now i'm experiencing problems my server is heavily loaded with 200-400 concurrent clients every second which is expected to grow over time.
which now looks like this
public ConcurrentHashMap<SocketChannel, UserProfile> clients = new ConcurrentHashMap<SocketChannel, UserProfile>();
My server design works like this.
I have a worker thread(s) for processing huge amounts of packets. Each packet is checked with a packetHandler sub-routine (not part of thread) pretty much any client can call it at anytime it's almost like static but it isn't.
My whole server is mostly single threaded except for the packet processing portion.
Anyways so when someone uses a command like count up all clients online and get some information from them.
It is also possible that clients can get disconnected and removed from ConcurrentHashMap while the counting is in progress (which causes my problems).
Also I'd like to add some code here.
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++;
}
}
}
I mean of course i'm going to fix it by adding a try-catch Exception inside the Iterator to skip these null clients.
But what I don't understand if it checks above if(person != null) shouldn't the code nested automatically works..
if it doesn't means it got removed while it was iterating which should be impossible since it's thread safe wtf?
What should I do? or is try-catch Exception the best way?
Here is the exception
java.lang.NullPointerException
at Server.processPackets(Server.java:398)
at PacketWorker.run(PacketWorker.java:43)
at java.lang.Thread.run(Thread.java:636)
The processPackets contains the code above. and the comment indicates the line count #
Thanks for enlightening me.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您需要阅读
ConcurrentHashMap.values()
方法,特别注意values()
集合的迭代器如何工作的描述:迭代器不会为您提供值集合状态的一致快照,但它是线程安全的,并且明确指定预期的行为范围。
如果您想要一个 Map 实现为您提供映射中值(或键或条目)的一致快照并允许您在修改的同时进行迭代,您可能需要创建一个自定义 Map 包装类(用于复制集合)原子地)...或成熟的自定义 Map 实现。对于您的用例来说,两者都可能比 ConcurrentHashMap 慢很多。
You need to read the javadocs for the
ConcurrentHashMap.values()
method, paying special attention to this description of how the iterator for thevalues()
collection works:The iterator does not give you a consistent snapshot of the state of the values collection, but it is thread-safe, and the expected range of behaviours is clearly specified.
If you want a Map implementation that gives you a consistent snapshot of the values (or keys, or entries) in the map AND allows you to iterate concurrently with modifications, you will probably need to create a custom Map wrapper class (that copies the collections atomically) ... or a full-blown custom Map implementation. Both are likely to be a lot slower than a ConcurrentHashMap for your use-case.
我没有看到你的代码有什么问题。由于崩溃不太可能实际发生在
else
处,因此getGender()
方法很可能返回null
。I don't see anything wrong with your code. Because it's unlikely that the crash actually occurs at the
else
, it's likely that thegetGender()
method is returningnull
.java.util.concurrent.ConcurrentHashMap不允许允许空值。因此,代码中的 null check(person != null) 是不必要的。
如果要在迭代时拒绝对Map进行修改,则必须在上述代码和所有修改操作代码中使用同步块。
java.util.concurrent.ConcurrentHashMap does not allow null value. So, null check(person != null) in your code is unnecessary.
If you want to deny modification of Map while iteration, you must use synchronization block in above code and all modification operation codes.
您可能会发现在迭代地图时无法修改地图。如果是这种情况,您可能希望获取单独集合中的值和键并对其进行迭代,因为它将是不可变的。
它并不完美,但另一个选择是扩展 ConcurrentHashMap,当添加或删除某些内容时,您会更新这四个变量,因此您不必每次都迭代整个列表,因为这似乎浪费了CPU周期。
这里有几个可能有用的链接:
这个链接讨论了并发性的提高是因为放松了一些承诺这一事实。
http://www.ibm.com/developerworks/java/library/ j-jtp07233.html
内存一致性属性解释:
http://下载-llnw.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility
You may find that you can't have the map get modified while you are iterating through it. If that is the case you may want to get the values and keys in a separate collection and iterate through that, as it will be immutable.
It won't be perfect, but the other option is to extend
ConcurrentHashMap
and when something is added or removed you update these four variables, so you don't have to iterate through the entire list each time, as that seems to be a waste of cpu cycles.Here are a couple of links that may be useful:
This one talks a bit about the fact that the improved concurrency is because of relaxing of some promises.
http://www.ibm.com/developerworks/java/library/j-jtp07233.html
memory consistency properties explained:
http://download-llnw.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility
ConcurrentHashMap 是线程安全的,它的函数也是线程安全的,但这并不意味着您使用 concurrenthashmap 的所有代码行也是线程安全的。
您必须同步的代码块。您正在迭代并尝试从并发哈希映射中获取对象,但在中间,其他一些线程有机会从同一并发哈希映射中删除对象,因此您的线程抛出 NPE。
ConcurrentHashMap is threadsafe and its functions are thread safe but it doesn't mean your all lines of code in which you are using concurrenthashmap are also thread safe.
The block of code you must synchronize. You are iterating and trying to get object from concurrenthashmap but in middle some other thread is getting chance to remove object from same concurrenthashmap due to which your thread is throwing NPE.