盲猜是因为多线程引起的,公共变量没更新 为什么 求解?

直接上代码

   /**

* 连接好的容器 使用volatile也不行

*/

private volatile ChannelFuture channelFuture;

@PostConstruct

public void start() {

//初始化信息

try {

this.init();

} catch (Exception e) {

log.error("启动 netty 客户端出现异常", e);

}

}

private void init() {

synchronized (this) {

//每次都需要重置通道

if (mChannel.get("channel") != null) {

mChannel.clear();

}

this.eventExecutors = new NioEventLoopGroup(1);

//配置客户端的各种信息

Bootstrap bootstrap = new Bootstrap();

//设置线程组

bootstrap.group(eventExecutors);

//设置通道 此通道是异步非堵塞

bootstrap.channel(NioSocketChannel.class);

bootstrap.option(ChannelOption.SO_KEEPALIVE, true);

//初始化通道 其中在里面配置逻辑

bootstrap.handler(new BdspNettySocketClientInitializer());

//连接设备信息

System.out.println("netty client start!");

this.channelFuture = bootstrap.connect("127.0.0.1", 6666);

this.channelFuture.addListener(new ConnectionListener());//这事防止客户端掉线 掉线后重连

}

}

public void send(String msg) {

try {

//这里是我找到的解决方法 思路是拿到最新的通道

//mChannel.get("channel").writeAndFlush(Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8));

//下面打印的地址第一次是一样的 当断线重启时 地址就不一样了

System.out.println(mChannel.get("channel"));

System.out.println(channelFuture.channel());

//这个通道在我使用第一次启动时可以使用 当断开时多线程重启时就不能使用原因是 我打印了下通道的地址 发现地址并没有更新 尝试给启动的配置加锁还是拿不到最新的通道 十分不解

channelFuture.channel().writeAndFlush(Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8));

} catch (Exception e) {

log.error(this.getClass().getName().concat(".send has error"), e);

}

}

重启的调用方式

    private BdspNettySocketClient bdspNettySocketClient = new BdspNettySocketClient();

private void reconnection(ChannelHandlerContext ctx) {

log.info("3s之后重新建立连接");

final EventLoop eventLoop = ctx.channel().eventLoop();

eventLoop.schedule(new Runnable() {

@Override

public void run() {

System.out.println("进行重连客户端");

bdspNettySocketClient.start();

}

}, 3L, TimeUnit.SECONDS);

}

根据下方两位回答的都试了 还是不行取到的还是第一次存放的数据 至于我那个取到最新的那个做法我是利用ConcurrentHashMap每次都存放(每次覆盖操作) 这样就是取出最新的

找到问题关键了
state 引发的血案
下面的个人对上面发生这种情况的理解 同时也为以后避免这种情况能正确的采用成员变量还是类变量 自我反省

代码执行流程 你会发现类变量和对象属性的不同

​ 第一步:连接netty服务端 得到一个连接对象

​ 第二步:拿到连接对象ChannelFuture 得到通道 发送消息 所以将ChannelFuture 变成类变量还是成员变量

​ 第三步:写一个发送的方法通过拿到的连接对象ChannelFuture发送消息

​ 第四步:完成当服务端断开后进行重连操作 (第二步选不对就会影响后面发送操作)

解释:

​ 1. 若采用成员变量则 保证调用发送消息的那个对象和操作重连的是同一个对象 否则你将拿不到最新的ChannelFuture(也就是重连后的连接对象) 只能拿到第一次初始化的旧变量

​ 2.若采用类变量则就不用考虑是不是同一个对象了


回答:

private AtomicReference<ChannelFuture> channelFutureRef = new AtomicReference<>();

this.channelFutureRef.set(bootstrap.connect("127.0.0.1", 6666));

ChannelFuture channelFuture = channelFutureRef.get();

channelFuture.channel().writeAndFlush(Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8));


回答:

channelFuture只在初始化时被设置一次,如果在其他线程中又调用start()方法进行重新连接,这个channelFuture是不会被更新的,在start()方法里调用init()是加锁的,这个锁是this对象锁,不同的线程获取的this是不同的,都能进入到init()方法中,但是channelFuture是类的成员变量,它在堆内存中只有一份,所以它被多个线程共享。
ChannelFuture是Netty的异步模型的核心,通过它可以在多线程环境下共享资源,当连接关闭、写请求完成等等事件发生时可以通知相关的线程。应该在init()方法里对channelFuture赋值的时候也加上同步处理,这样才能保证每个线程都能拿到最新的channelFuture。

private void init() {

synchronized (this) {

//每次都需要重置通道

if (mChannel.get("channel") != null) {

mChannel.clear();

}

this.eventExecutors = new NioEventLoopGroup(1);

//配置客户端的各种信息

Bootstrap bootstrap = new Bootstrap();

//设置线程组

bootstrap.group(eventExecutors);

//设置通道 此通道是异步非堵塞

bootstrap.channel(NioSocketChannel.class);

bootstrap.option(ChannelOption.SO_KEEPALIVE, true);

//初始化通道 其中在里面配置逻辑

bootstrap.handler(new BdspNettySocketClientInitializer());

//连接设备信息

System.out.println("netty client start!");

// 保证channelFuture在多线程环境下的安全性

synchronized (BdspNettySocketClient.class) {

this.channelFuture = bootstrap.connect("127.0.0.1", 6666);

}

this.channelFuture.addListener(new ConnectionListener());//这事防止客户端掉线 掉线后重连

}

}

保证每次调用init()方法时,都能获取到最新的channelFuture。这里的锁对象是BdspNettySocketClient.class,要保护的资源是channelFuture,它是所有BdspNettySocketClient对象共享的,所以在类级别上对它进行同步。如果使用this作为锁对象,只能保证在同一个对象中的多个线程间的同步,无法处理多个对象之间的线程同步问题。

以上是 盲猜是因为多线程引起的,公共变量没更新 为什么 求解? 的全部内容, 来源链接: utcz.com/p/945259.html

回到顶部