记录一次非常典型的可见性引起的多线程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,所以对性能几乎没有影响。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值