记录一次非常典型的可见性引起的多线程BUG,用volatile解决

在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,所以对性能几乎没有影响。





猜你喜欢

转载自blog.csdn.net/bysoulwarden/article/details/80841641
今日推荐