When learning Netty, you will certainly see these codes:
//Start server listening ChannelFuture future = serverBootstrap.bind(7000).sync(); //close? future.channel().closeFuture().sync();
- Question 1: the object returned by serverBootstrap.bind() is ChannelFuture. What is the effect of calling its sync() method?
- Question 2: what exactly does channel.closeFuture().sync() do?
OK, let's analyze these two questions step by step
2. Principle analysis2.1 function of channelfuture calling sync()
When the service binds the port with bind, it will register the Channel with NioEventLoop. At this time, a DefaultChannelPromise object will be created and returned
//SingleThreadEventLoop.java public ChannelFuture register(Channel channel) { return register(new DefaultChannelPromise(channel, this)); }
There is an inheritance relationship between DefaultChannelPromise and ChannelFuture:
As mentioned in the previous article, NioEventLoop is a single threaded thread pool, and the whole registration is processed through asynchronous threads. So let's make a bold guess: is sync() called to block and wait for the registration to end?
Take a look at the source code of sync:
//DefaultPromise.java public Promise<V> sync() throws InterruptedException { await(); rethrowIfFailed(); return this; }
Here is a await(), which translates to wait, so keep tracking:
- isDone(): each DefaultPromise has a result attribute to mark the operation state of the current object
- wait(): Object base class method. If isDone() returns false, the current thread will enter the blocking state until other threads call notify/notifyAll to wake up
If the blocked place is found, there must be a place to wake up. The answer is in the safeSetSuccess() method, which will be called after successful registration:
//AbstractChannel.java protected final void safeSetSuccess(ChannelPromise promise) { if (!(promise instanceof VoidChannelPromise) && !promise.trySuccess()) { logger.warn("Failed to mark a promise as success because it is done already: {}", promise); } }
Here is a promise.trySuccess(), the bottom layer will call setValue0 to set result = SUCCESS, and execute notifyAll() to wake up the thread:
Attach checkNotifyWaiters Code:
private synchronized boolean checkNotifyWaiters() { if (waiters > 0) { notifyAll(); } return listeners != null; }
Summary: sync will cause the current execution thread to enter the blocking state until the asynchronous thread calls notify/notifyAll of the ChannelFuture object to wake up. This time is usually triggered after the execution of the business logic represented by ChannelFuture
2.2 what is closeFuture() called by channel
closeFuture() returns an object of type CloseFuture, which is one of the properties of the Channel.
CloseFuture inherits DefaultChannelPromise. It has only one setClosed() method. Calling other methods will throw exceptions:
static final class CloseFuture extends DefaultChannelPromise { CloseFuture(AbstractChannel ch) { super(ch); } @Override public ChannelPromise setSuccess() { throw new IllegalStateException(); } @Override public ChannelPromise setFailure(Throwable cause) { throw new IllegalStateException(); } @Override public boolean trySuccess() { throw new IllegalStateException(); } @Override public boolean tryFailure(Throwable cause) { throw new IllegalStateException(); } boolean setClosed() { return super.trySuccess(); } }
By tracing setClosed, you can find that this method is called when an exception occurs. At this time, the blocking caused by closeFuture.sync() will be awakened.
Conclusion: closeFuture.sync() is used to block the current thread and prevent the program from ending directly