The result of an asynchronous
ChannelGroup operation.
ChannelGroupFuture is composed of
ChannelFutures which
represent the outcome of the individual I/O operations that affect the
Channels in the
ChannelGroup.
All I/O operations in
ChannelGroup are asynchronous. It means any
I/O calls will return immediately with no guarantee that the requested I/O
operations have been completed at the end of the call. Instead, you will be
returned with a
ChannelGroupFuture instance which tells you when the
requested I/O operations have succeeded, failed, or cancelled.
Various methods are provided to let you check if the I/O operations has been
completed, wait for the completion, and retrieve the result of the I/O
operation. It also allows you to add more than one
ChannelGroupFutureListener so you can get notified when the I/O
operation have been completed.
Prefer
#addListener(ChannelGroupFutureListener) to
#await()
It is recommended to prefer
#addListener(ChannelGroupFutureListener) to
#await() wherever possible to get notified when I/O operations are
done and to do any follow-up tasks.
#addListener(ChannelGroupFutureListener) is non-blocking. It simply
adds the specified
ChannelGroupFutureListener to the
ChannelGroupFuture, and I/O thread will notify the listeners when
the I/O operations associated with the future is done.
ChannelGroupFutureListener yields the best performance and resource
utilization because it does not block at all, but it could be tricky to
implement a sequential logic if you are not used to event-driven programming.
By contrast,
#await() is a blocking operation. Once called, the
caller thread blocks until all I/O operations are done. It is easier to
implement a sequential logic with
#await(), but the caller thread
blocks unnecessarily until all I/O operations are done and there's relatively
expensive cost of inter-thread notification. Moreover, there's a chance of
dead lock in a particular circumstance, which is described below.
Do not call
#await() inside
ChannelHandler
The event handler methods in
ChannelHandler is often called by
an I/O thread unless an
ExecutionHandler is in the
ChannelPipeline. If
#await() is called by an event handler
method, which is called by the I/O thread, the I/O operation it is waiting
for might never be complete because
#await() can block the I/O
operation it is waiting for, which is a dead lock.
// BAD - NEVER DO THIS
@Overridepublic void messageReceived(
ChannelHandlerContext ctx,
MessageEvent e) {
if (e.getMessage() instanceof ShutdownMessage) {
ChannelGroup allChannels = MyServer.getAllChannels();
ChannelGroupFuture future = allChannels.close();
future.awaitUninterruptibly();
// Perform post-shutdown operation
// ...
}
}
// GOOD
@Overridepublic void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
if (e.getMessage() instanceof ShutdownMessage) {
ChannelGroup allChannels = MyServer.getAllChannels();
ChannelGroupFuture future = allChannels.close();
future.addListener(new
ChannelGroupFutureListener() {
public void operationComplete(
ChannelGroupFuture future) {
// Perform post-closure operation
// ...
}
});
}
}
In spite of the disadvantages mentioned above, there are certainly the cases
where it is more convenient to call
#await(). In such a case, please
make sure you do not call
#await() in an I/O thread. Otherwise,
IllegalStateException will be raised to prevent a dead lock.