在la-rpc consumer最初使用时,RpcProxy新建一个client,并调用send,使用channel发送信息。
if(RpcClient.client==null){
synchronized (RpcClient.class){
if(RpcClient.client==null)
RpcClient.client=new RpcClient();
}
}
RpcClient.client.send(requestArray);
RpcClient()会在NioEventLoop线程中新建连接,设置channel。
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("ProxyHandler:new channel");
rpcClient.setChannel(ctx.channel());
}
最开始,程序正常工作,但当我完善心跳这部分时,先运行一次consumer,一切正常,再关闭进程重新运行时,总会出现channel为null的异常。
此时设置:若channel为null,进行轮询,直到channel不为null为止。再运行consumer线程会一直轮询。
套用java内存模型,在NioEventLoop线程将channel的新值写回主内存前,consumer线程就取出了channel。
(后来我又做了实验,每次使用channel都会调用字节码getfield,如果使用轮询,只会调用一次getfield。加入volatile后,属性表code无变化)
所以这是一个典型的可见性问题,并且是我见过最典型的一例。
最后,将channel设置成volatile可以解决问题。
关于性能的影响:client只有初始化时会更新channel,之后一直复用此channel,所以对性能几乎没有影响。