
昨天在 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 会出现并发问题。。。
请大神指导!!
1 nazor 2019-06-12 07:44:09 +08:00 via iPhone remove 空指针,是因为 hashmap 为 null |
2 temp178 2019-06-12 07:45:39 +08:00 1. player 是如何生成的?是否会有多个 handler 去 remove 同一个 player 的情况,这种情况可能会导致 null 异常 |
3 temp178 2019-06-12 07:52:20 +08:00 2.看了#1 的回复,楼主确定下到底是因为什么为 null 导致的 null 异常?或者 debug 一下? |
4 nazor 2019-06-12 07:54:05 +08:00 via iPhone 确定不是 geuNum 返回 null? player 为 null,执行不到 remove 吧 |
6 gramyang OP @mejee player 是 handler 的私有变量,concurrenthashmap 是 handler 外部传入的变量。会有 concurrenthashmap 同时 remove 多个 player 的情况 |
8 luckylo 2019-06-12 08:03:20 +08:00 via Android @gramyang 应该是 map 本身为空。remove 会返回 remove 的值,如果没有对应的 key,应该不会报空指针,最多应该就是返回 null。 |
9 gramyang OP |
10 luckylo 2019-06-12 08:08:37 +08:00 via Android |
12 temp178 2019-06-12 08:16:58 +08:00 |
13 YzSama 2019-06-12 08:32:49 +08:00 via iPhone show me the code。XD |
15 xuanbg 2019-06-12 09:11:02 +08:00 好多个 remove,到底是哪一行抛了空指针? |
16 anzu 2019-06-12 09:41:03 +08:00 handleAfterExitOrException 没有锁,当并发执行的时候,player 随时会被其它线程置 null,检查是否为 null 没用。 |
17 passerbytiny 2019-06-12 10:00:16 +08:00 不太确定没有 sharable 注解的时候,handler 就是单个连接通道独占的。问题可能出在这里。 |
18 Macolor21 2019-06-12 10:01:40 +08:00 代码是 playerMap.remove( player.getSeatNum() ); 这里抛出空指针异常,要不就是 map 空,要不就是 player 空,标题起的有歧义,应该是执行 apiHandler.exitOrException();时,player 被其他线程置 null |
19 passerbytiny 2019-06-12 10:05:55 +08:00 这里建议用 ChannelContext 或者 Channel 的属性去保存 player,它们确定是线程安全或者单个通道独享的。 |
20 cookii 2019-06-12 10:07:15 +08:00 楼主说了,Handler 没有 sharable,所以 Handler 不会并发被调用,一个 handler 总是在同一个线程中被执行。所以在同一个线程中,就不存在重排序的问题。这个问题看起来比较诡异,建议打断点观看变量的值。 |
22 gramyang OP @imzhoukunqiang 是的,很诡异。说实话,上面的代码已经是我修改过了的,不过意思没变,都是很诡异的空指针。 |
23 passerbytiny 2019-06-12 10:25:51 +08:00 去翻了一下 https://netty.io/4.0/api/io/netty/channel/ChannelHandler.Sharable.html,没有 Sharable 的时候,Handle 是单个通道独占的。 到目前为止,根据楼主已放出来的消息,找不出其他原因了。 |
24 gramyang OP @passerbytiny 也足够了,起码帮助排除了重排序和并发错误的可能性。修改代码后如果再出现这种错误再另说 |
25 firefffffffffly 2019-06-12 10:41:50 +08:00 建议把 exception 信息贴出来,这样能轻松确定是 map 为空还是传入的 key 值为空。 从描述的 exception 来看 player 最不可能为空,因为这样的话报错 message 和 traces 里是不会包含 remove 相关内容的,因为在 player.getNum()时就会报错了,remove 函数还没有入栈。 key 值为空的情况,就是 player.getNum()的结果为 null,这个 player 内部属性需要再检查一下是否有多线程修改。 |
26 rainmakeroly 2019-06-12 10:44:57 +08:00 via Android player 的获取,设置,初始化。报错信息的话主要是它吧 |
27 alamaya 2019-06-12 11:48:40 +08:00 你这个 apiHandler 是怎么来的?没看出来你的 player 是怎么传入的 |
28 senninha 2019-06-12 16:34:55 +08:00 - -player 在其他线程并发置 null 了?有其他线程在操作这个 player ?如果其他线程要操作,可以丢到 eventloop 里转成同步执行,保证并发安全。 ps:直接在 handler 里写业务代码的吗?这么强悍。。 |
29 laodao1990 2019-06-13 16:11:12 +08:00 要不这样试试: if player!=null 锁{ if player!=null { remove } } |