盲猜是因为多线程引起的,公共变量没更新 为什么 求解?
直接上代码
/** * 连接好的容器 使用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