ChannelFuture的做用是用來保存Channel異步操做的結果。編程
咱們知道,在Netty中全部的I/O操做都是異步的。這意味着任何的I/O調用都將當即返回,而不保證這些被請求的I/O操做在調用結束的時候已經完成。取而代之地,你會獲得一個返回的ChannelFuture實例,這個實例將給你一些關於I/O操做結果或者狀態的信息。異步
對於一個ChannelFuture可能已經完成,也可能未完成。當一個I/O操做開始的時候,一個新的future對象就會被建立。在開始的時候,新的future是未完成的狀態--它既非成功
、失敗
,也非被取消
,由於I/O操做尚未結束。若是I/O操做以成功
、失敗
或者被取消
中的任何一種狀態結束了,那麼這個future將會被標記爲已完成
,幷包含更多詳細的信息(例如:失敗的緣由)。請注意,即便是失敗
和被取消
的狀態,也是屬於已完成
的狀態。ide
下面這張圖來自於官方文檔,用於說明各類狀態的關係:post
ChannelFuture狀態關係性能
各類各樣的方法被提供,用來檢查I/O操做是否已完成、等待完成,並尋回I/O操做的結果。它一樣容許你添加ChannelFutureListener,以便於在I/O操做完成的時候,你可以得到通知。spa
當作了一個I/O操做並有任何後續任務的時候,推薦優先使用addListener(GenericFutureListener)的方式來得到通知,而非await()。線程
addListener(GenericFutureListener)是非阻塞的。它會把特定的ChannelFutureListener添加到ChannelFuture中,而後I/O線程會在I/O操做相關的future完成的時候通知監聽器。ChannelFutureListener會利於最佳的性能和資源的利用,由於它一點阻塞都沒有。然而,若是你不使用基於事件驅動的編程方式,去實現一個後續式的邏輯會變得詭異和難於理解。code
對比來看,await()是一個阻塞的操做。一旦被調用,調用者線程會阻塞,直到操做完成。使用await()來實現一個後續式的邏輯會更容易,可是調用者線程會很是不必的阻塞直到I/O操做完成,而且內部的線程通知是相對來講代價昂貴的。更有甚者,在一些特定的狀況下會產生死鎖,下面是對這種狀況的描述:orm
// BAD - NEVER DO THIS @Override public void channelRead(ChannelHandlerContext ctx, GoodByeMessage msg) { ChannelFuture future = ctx.channel().close(); future.awaitUninterruptibly(); // Perform post-closure operation // ... } // GOOD @Override public void channelRead(ChannelHandlerContext ctx, GoodByeMessage msg) { ChannelFuture future = ctx.channel().close(); future.addListener(new ChannelFutureListener() { public void operationComplete(ChannelFuture future) { // Perform post-closure operation // ... } }); }
撇開上面提到的缺點不談,確實仍是有一些狀況在調用await()的時候會更方便的。在這種狀況下,請確保你不是在一個I/O線程中調用的await()。不然,爲了不死鎖的狀況,BlockingOperationException將被提出。對象
你在使用Future.await(long), Future.await(long, TimeUnit),Future.awaitUninterruptibly(long),或者Future.awaitUninterruptibly(long, TimeUnit)的時候,指定的timeout的值和I/O timeout一點關係都沒有。若是一個操做超時了,future將會被標記爲已完成-失敗
,就像上面的圖中描述的那樣。例如,鏈接超時應當經過一個傳輸特定的選項來配置:
// BAD - NEVER DO THIS Bootstrap b = ...; ChannelFuture f = b.connect(...); f.awaitUninterruptibly(10, TimeUnit.SECONDS); if (f.isCancelled()) { // Connection attempt cancelled by user } else if (!f.isSuccess()) { // You might get a NullPointerException here because the future // might not be completed yet. f.cause().printStackTrace(); } else { // Connection established successfully } // GOOD Bootstrap b = ...; // Configure the connect timeout option. b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000); ChannelFuture f = b.connect(...); f.awaitUninterruptibly(); // Now we are sure the future is completed. assert f.isDone(); if (f.isCancelled()) { // Connection attempt cancelled by user } else if (!f.isSuccess()) { f.cause().printStackTrace(); } else { // Connection established successfully }