昨天在 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
mejee 2019-06-12 07:45:39 +08:00
1. player 是如何生成的?是否会有多个 handler 去 remove 同一个 player 的情况,这种情况可能会导致 null 异常
|
3
mejee 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
mejee 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 } } |