【问题标题】:How Netty ChannelFuture notify does not cause dead lockNetty ChannelFuture notify 如何不导致死锁
【发布时间】:2017-06-06 23:38:35
【问题描述】:

我已经阅读了 Netty 指南,它在 ChannelFuture 上没有解释太多。我很困惑为什么它不会导致死锁。

1.它教我像这样启动服务器。

ServerBootstrap sb = new ServerBootstrap();
        sb.group(bossGroup, workerGroup);
        sb.channel(NioServerSocketChannel.class);
        sb.localAddress(new InetSocketAddress(port));
        sb.childHandler(new ChannelInitializer<SocketChannel>() {

                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new BigGameMessageDecoder(msgRegistry));
                        ch.pipeline().addLast(BIG_MSG_ENCODER);
                    if (isDebug) {
                        ch.pipeline().addLast(MSG_LOGGER);
                    }

                    ch.pipeline().addLast(new GameMessageHandler<>(msgRegistry,
                            sessionFactory.createGameSession(), event, false));
                } 

        });

        ChannelFuture cf = sb.bind().sync();
        logger.error("start server at port: {}", port);
        if (sync) {
            cf.channel().closeFuture().sync();
        }

排队:

ChannelFuture cf = sb.bind().sync();

sb.bind() 返回一个ChannelFuturesync() 将等到这个未来完成。

我阅读了DefaultChannelGroupFuture 代码,它告诉我sync() 确实调用了await()。 并且await()锁定自己,等待别人的通知。

ChannelFuture 的函数setSuccess 中,它会再次尝试获取该锁。所以我的问题是sync() 是否先获得锁然后等待然后ChannelFuture 尝试通知但它无法获得锁。会不会造成死锁?

  1. 如果不这样做,ChannelFuture如何通知其他听众?

  2. 其他书籍告诉我不要在ChannelHandler 中使用sync()await(),因为这可能会导致死锁。为什么?问题 1 和问题 3 有什么区别?


public DefaultChannelGroupFuture sync() throws InterruptedException {
    super.sync();
    return this;
}

  public Promise<V> sync() throws InterruptedException {
    await();
    rethrowIfFailed();
    return this;
}


  public Promise<V> await() throws InterruptedException {
    if (isDone()) {
        return this;
    }

    if (Thread.interrupted()) {
        throw new InterruptedException(toString());
    }

    synchronized (this) {
        while (!isDone()) {
            checkDeadLock();
            incWaiters();
            try {
                wait();
            } finally {`enter code here`
                decWaiters();
            }
        }
    }
    return this;
}

  public Promise<V> setSuccess(V result) {
    if (setSuccess0(result)) {
        notifyListeners();
        return this;
    }
    throw new IllegalStateException("complete already: " + this);
}



  private boolean setSuccess0(V result) {
    if (isDone()) {
        return false;
    }

    synchronized (this) {
        // Allow only once.
        if (isDone()) {
            return false;
        }
        if (result == null) {
            this.result = SUCCESS;
        } else {
            this.result = result;
        }
        if (hasWaiters()) {
            notifyAll();
        }
    }
    return true;
}

【问题讨论】:

    标签: java netty future


    【解决方案1】:

    在你上面的代码中:

    public Promise<V> await() throws InterruptedException {
    if (isDone()) {
        return this;
    }
    
    if (Thread.interrupted()) {
        throw new InterruptedException(toString());
    }
    
    synchronized (this) {
        while (!isDone()) {
            checkDeadLock();
            incWaiters();
            try {
                wait();
            } finally {`enter code here`
                decWaiters();
            }
        }
    }
    return this;
    

    sync方法代码checkDeadLock();会检查当前线程不是用于处理io事件的内部线程,如果不是,则会发生死锁,而绑定操作将分派到等待锁的同一个线程然后wait();会释放this的锁,等待某个线程获取锁并通知它。当IO线程调用setSuccess时,它可以获取锁,因为没有人持有锁.

    【讨论】:

    • 所以如果在我的channelHandler方法channelInactive尝试像这样重新连接同步bootstrap.connect().sync();bootstrap是连接服务器的带子。根据我们上面的结论,它肯定会抛出异常。但实际上,异常的发生是偶然的。 io.netty.util.concurrent.BlockingOperationException: DefaultChannelPromise@3b72750a(incomplete) at io.netty.util.concurrent.DefaultPromise.checkDeadLock(DefaultPromise.java:380) ~[netty-all-4.0.13.Final.jar:4.0.13.Final] at io.netty.channel.DefaultChannelPromise.checkDeadLock(DefaultChann
    • @user8119590 因为awai()在操作完成时没有等待锁。你可以执行代码bootstrap.connect().channel().closeFuture().sync(),看看发生了什么。
    • 我明白了。这要看bootstrap.connect()的未来反应快不快。当执行sync() if isDone() return true 方法会返回,所以没有异常。如果isDone() 返回false 那么方法将检查死锁。 @大白菜
    • @wangcaicai 没错,isDone() 是关键方法。