ConcurrentHashMap 的使用问题

2019-06-12 07:41:42 +08:00
 gramyang

昨天在 netty 的 handler 里碰到了一个非常奇怪的问题: 1、首先,handler 没有加 sharable 注解 2、我在 handler 的外部生成了一个 concurrenthashmap 实例并传入 handler 3、在 handler 的一个方法中调用 concurrenthashmap 的 remove(player.getNum()),然后再调用 player=null 将 player 清空。

这个时候奇迹出现了,remove 报空指针,也就是 remove 的时候 player 是 null。 我检查完了所有的代码,再没有其他地方把 player 设置为 null,并且 remove 的操作前还判断了 player!=null。

思来想去,只有两个可能: Java 中的指令重排序,导致 player 在 remove 之前就被空置,但是感觉不太可能啊。。。 concurrenthashmap 是多个线程共享的变量,直接 remove 会出现并发问题。。。

请大神指导!!

4197 次点击
所在节点    Java
29 条回复
nazor
2019-06-12 07:44:09 +08:00
remove 空指针,是因为 hashmap 为 null
mejee
2019-06-12 07:45:39 +08:00
1. player 是如何生成的?是否会有多个 handler 去 remove 同一个 player 的情况,这种情况可能会导致 null 异常
mejee
2019-06-12 07:52:20 +08:00
2.看了#1 的回复,楼主确定下到底是因为什么为 null 导致的 null 异常?或者 debug 一下?
nazor
2019-06-12 07:54:05 +08:00
确定不是 geuNum 返回 null? player 为 null,执行不到 remove 吧
gramyang
2019-06-12 07:56:55 +08:00
@nazor 不会吧,不可能啊,remove 出空指针不是 remove 传入的变量为 null 吗?
gramyang
2019-06-12 07:58:53 +08:00
@mejee player 是 handler 的私有变量,concurrenthashmap 是 handler 外部传入的变量。会有 concurrenthashmap 同时 remove 多个 player 的情况
gramyang
2019-06-12 07:59:50 +08:00
@nazor 不会,因为前面代码有 player!=null 和 player.getNum>1 的判断
luckylo
2019-06-12 08:03:20 +08:00
@gramyang 应该是 map 本身为空。remove 会返回 remove 的值,如果没有对应的 key,应该不会报空指针,最多应该就是返回 null。
gramyang
2019-06-12 08:06:29 +08:00
@luckylo 代码层面上,map 不可能为空,我是初始化之后才传进去的。另外 concurrenthashmap 的 remove 方法源码上的注释:
@throws NullPointerException if the specified key is null
luckylo
2019-06-12 08:08:37 +08:00
luckylo
2019-06-12 08:09:11 +08:00
@gramyang 我刚去翻文档了😂
mejee
2019-06-12 08:16:58 +08:00
@luckylo
@gramyang 刚去验证了下,
@luckylo 说的对,没有对应 key 不会报 null 异常,是我记错了
YzSama
2019-06-12 08:32:49 +08:00
show me the code。XD
gramyang
2019-06-12 08:42:14 +08:00
@YzSama 贴在问题后面了,但是代码很多很杂,还是文字描述更精炼一些
xuanbg
2019-06-12 09:11:02 +08:00
好多个 remove,到底是哪一行抛了空指针?
anzu
2019-06-12 09:41:03 +08:00
handleAfterExitOrException 没有锁,当并发执行的时候,player 随时会被其它线程置 null,检查是否为 null 没用。
passerbytiny
2019-06-12 10:00:16 +08:00
不太确定没有 sharable 注解的时候,handler 就是单个连接通道独占的。问题可能出在这里。
Macolor21
2019-06-12 10:01:40 +08:00
代码是 playerMap.remove( player.getSeatNum() );
这里抛出空指针异常,要不就是 map 空,要不就是 player 空,标题起的有歧义,应该是执行 apiHandler.exitOrException();时,player 被其他线程置 null
passerbytiny
2019-06-12 10:05:55 +08:00
这里建议用 ChannelContext 或者 Channel 的属性去保存 player,它们确定是线程安全或者单个通道独享的。
cookii
2019-06-12 10:07:15 +08:00
楼主说了,Handler 没有 sharable,所以 Handler 不会并发被调用,一个 handler 总是在同一个线程中被执行。所以在同一个线程中,就不存在重排序的问题。这个问题看起来比较诡异,建议打断点观看变量的值。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/573009

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX