深入浅出 Netty 中的 Future 与 Promise
前言
在学习 Netty 的过程中,你一定会遇到 Future 和 Promise 这两个概念。它们是 Netty 异步编程的核心,理解它们对掌握 Netty 至关重要。
本文将从 JDK 的 Future 开始,逐步引出 Netty 对 Future 的增强,最后介绍 Promise 的作用。通过对比和实例,让你彻底理解这三者的关系和使用场景。
一、从 JDK Future 说起
1. 什么是 Future?
想象一下这个场景:你去餐厅点餐,服务员给你一个取餐号码牌。这个号码牌就是 Future,它代表了一个"未来会完成的任务"。你可以拿着号码牌去做其他事情,等餐做好了再来取。
在 Java 中,Future 就是这样一个"号码牌",它代表一个异步任务的执行结果。
2. JDK Future 接口定义
package java.util.concurrent;
public interface Future<V> {
// 取消任务执行
boolean cancel(boolean mayInterruptIfRunning);
// 任务是否已取消
boolean isCancelled();
// 任务是否已完成(正常完成、异常、取消都算完成)
boolean isDone();
// 阻塞获取任务执行结果(会一直等待,直到任务完成)
V get() throws InterruptedException, ExecutionException;
// 带超时的阻塞获取(最多等待指定时间)
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
3. JDK Future 使用示例
ExecutorService executor = Executors.newSingleThreadExecutor();
// 提交一个异步任务
Future<String> future = executor.submit(() -> {
Thread.sleep(2000); // 模拟耗时操作
return "任务完成";
});
System.out.println("任务已提交,继续做其他事情...");
// 阻塞等待结果
String result = future.get(); // 这里会阻塞 2 秒
System.out.println("获取到结果:" + result);
4. JDK Future 的局限性
虽然 JDK 的 Future 提供了异步能力,但它有两个明显的问题:
问题 1:只能阻塞获取结果
回到餐厅的例子,JDK Future 就像你必须站在取餐口一直等着,不能离开。你调用 get() 方法时,当前线程会被阻塞,直到任务完成。
// 这行代码会阻塞当前线程,直到任务完成
String result = future.get();
问题 2:没有回调机制
如果餐厅能在餐做好时给你发个短信通知,你就不用一直等着了。但 JDK Future 没有这种"通知"机制,你必须主动去查询(调用 get())。
这就是为什么 Netty 要对 Future 进行增强!
二、Netty 对 Future 的增强
1. Netty Future 解决了什么问题?
Netty 继承了 JDK 的 Future 接口,并在此基础上做了大量增强。就像给餐厅的取餐系统升级了:
- 增加了短信通知功能(回调机制)
- 可以随时查看餐是否做好,不用一直等(非阻塞查询)
- 可以知道餐做失败的原因(失败原因查询)
2. Netty Future 接口定义
package io.netty.util.concurrent;
public interface Future<V> extends java.util.concurrent.Future<V> {
// ========== 状态查询(比 JDK 更详细)==========
boolean isSuccess(); // 是否成功完成(区分成功和失败)
boolean isCancellable(); // 是否可取消
Throwable cause(); // 失败原因(如果失败了,可以知道为什么)
// ========== 非阻塞获取(这是新增的!)==========
V getNow(); // 立即获取结果,不阻塞
// 如果任务未完成,返回 null
// ========== 阻塞等待(不抛异常)==========
Future<V> await() throws InterruptedException;
Future<V> awaitUninterruptibly();
boolean await(long timeout, TimeUnit unit) throws InterruptedException;
boolean await(long timeoutMillis) throws InterruptedException;
// ========== 阻塞等待(抛异常)==========
Future<V> sync() throws InterruptedException; // 如果任务失败,会抛出异常
Future<V> syncUninterruptibly();
// ========== 监听器(回调机制,这是核心增强!)==========
Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener);
Future<V> addListeners(GenericFutureListener<? extends Future<? super V>>... listeners);
Future<V> removeListener(GenericFutureListener<? extends Future<? super V>> listener);
Future<V> removeListeners(GenericFutureListener<? extends Future<? super V>>... listeners);
// ========== 取消操作 ==========
@Override
boolean cancel(boolean mayInterruptIfRunning);
}
3. Netty Future 的核心增强点
增强 1:回调机制(最重要!)
这是 Netty Future 最重要的增强。你可以添加监听器,任务完成后会自动通知你。
// Netty 客户端连接示例
ChannelFuture future = bootstrap.connect("localhost", 8080);
// 添加监听器,连接完成后自动回调
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
System.out.println("连接成功!");
} else {
System.out.println("连接失败:" + future.cause().getMessage());
}
}
});
// 主线程可以继续做其他事情,不用阻塞等待
System.out.println("连接请求已发出,继续执行其他任务...");
使用 Lambda 表达式更简洁:
future.addListener(f -> {
if (f.isSuccess()) {
System.out.println("连接成功!");
} else {
System.out.println("连接失败:" + f.cause().getMessage());
}
});
增强 2:非阻塞获取结果
// 立即获取结果,不阻塞
Channel channel = future.getNow();
if (channel != null) {
System.out.println("连接已完成");
} else {
System.out.println("连接还未完成");
}
增强 3:更详细的状态查询
if (future.isDone()) {
// 任务已完成(可能成功、失败或取消)
if (future.isSuccess()) {
// 成功完成
System.out.println("任务成功");
} else if (future.isCancelled()) {
// 被取消
System.out.println("任务被取消");
} else {
// 失败了
System.out.println("任务失败:" + future.cause().getMessage());
}
}
增强 4:await() vs sync()
这两个方法都是阻塞等待,但有区别:
// await():只等待任务完成,不管成功还是失败
future.await();
if (future.isSuccess()) {
// 需要手动检查是否成功
}
// sync():等待任务完成,如果失败会抛出异常
try {
future.sync(); // 如果连接失败,这里会抛异常
System.out.println("连接成功");
} catch (Exception e) {
System.out.println("连接失败:" + e.getMessage());
}
4. Netty Future 使用场景
在 Netty 中,几乎所有的异步操作都会返回 Future:
// 绑定端口
ChannelFuture bindFuture = serverBootstrap.bind(8080);
// 连接服务器
ChannelFuture connectFuture = bootstrap.connect("localhost", 8080);
// 写数据
ChannelFuture writeFuture = channel.writeAndFlush(msg);
// 关闭连接
ChannelFuture closeFuture = channel.close();
三、Promise:可写的 Future
1. 什么是 Promise?
如果说 Future 是"取餐号码牌",那么 Promise 就是"后厨的订单单"。
- Future:只能读取结果,不能修改(顾客只能等待和查询)
- Promise:既能读取结果,也能写入结果(后厨可以标记订单完成或失败)
在 Netty 中,Promise 继承自 Future,增加了设置结果的能力。
2. Promise 接口定义
package io.netty.util.concurrent;
public interface Promise<V> extends Future<V> {
// ========== 设置成功结果 ==========
Promise<V> setSuccess(V result); // 设置成功,如果已经设置过会抛异常
boolean trySuccess(V result); // 尝试设置成功,如果已设置过返回 false
// ========== 设置失败结果 ==========
Promise<V> setFailure(Throwable cause); // 设置失败,如果已经设置过会抛异常
boolean tryFailure(Throwable cause); // 尝试设置失败,如果已设置过返回 false
// ========== 设置不可取消 ==========
boolean setUncancellable();
// ========== 继承自 Future 的方法(返回类型改为 Promise,支持链式调用)==========
@Override
Promise<V> addListener(GenericFutureListener<? extends Future<? super V>> listener);
@Override
Promise<V> await() throws InterruptedException;
@Override
Promise<V> sync() throws InterruptedException;
// ... 其他方法
}
3. Promise 使用示例
示例 1:基本使用
// 创建一个 Promise
EventLoop eventLoop = new NioEventLoopGroup().next();
Promise<String> promise = eventLoop.newPromise();
// 添加监听器
promise.addListener(future -> {
if (future.isSuccess()) {
System.out.println("任务成功:" + future.getNow());
} else {
System.out.println("任务失败:" + future.cause().getMessage());
}
});
// 在另一个线程中执行任务
new Thread(() -> {
try {
Thread.sleep(2000);
// 设置成功结果
promise.setSuccess("任务完成!");
} catch (Exception e) {
// 设置失败结果
promise.setFailure(e);
}
}).start();
System.out.println("任务已提交,继续执行...");
示例 2:try 方法的使用
Promise<String> promise = eventLoop.newPromise();
// trySuccess 不会抛异常
if (promise.trySuccess("第一次设置")) {
System.out.println("设置成功");
}
// 再次尝试设置,会返回 false
if (!promise.trySuccess("第二次设置")) {
System.out.println("已经设置过了,无法再次设置");
}
// 如果使用 setSuccess,第二次会抛异常
// promise.setSuccess("第二次设置"); // 会抛 IllegalStateException
示例 3:在 Netty 中的实际应用
public class MyHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) {
// 创建一个 Promise
Promise<String> promise = ctx.executor().newPromise();
// 添加监听器
promise.addListener(future -> {
if (future.isSuccess()) {
// 发送响应
ctx.writeAndFlush(future.getNow());
} else {
// 关闭连接
ctx.close();
}
});
// 异步处理业务逻辑
processAsync(promise);
}
private void processAsync(Promise<String> promise) {
// 模拟异步业务处理
new Thread(() -> {
try {
// 业务逻辑
String result = doSomething();
promise.setSuccess(result);
} catch (Exception e) {
promise.setFailure(e);
}
}).start();
}
}
4. set vs try 方法的区别
| 方法 | 行为 | 使用场景 |
|---|---|---|
setSuccess(V) | 设置成功,如果已设置会抛异常 | 确定只会设置一次的场景 |
trySuccess(V) | 尝试设置成功,如果已设置返回 false | 可能多次尝试设置的场景 |
setFailure(Throwable) | 设置失败,如果已设置会抛异常 | 确定只会设置一次的场景 |
tryFailure(Throwable) | 尝试设置失败,如果已设置返回 false | 可能多次尝试设置的场景 |
// 使用 set 方法(确定只设置一次)
promise.setSuccess("结果");
// 使用 try 方法(可能多次尝试)
if (promise.trySuccess("结果")) {
// 设置成功
} else {
// 已经被其他线程设置过了
}
四、三者关系总结
1. 继承关系
java.util.concurrent.Future (JDK)
↑
|
io.netty.util.concurrent.Future (Netty)
↑
|
io.netty.util.concurrent.Promise (Netty)
2. 功能对比
| 特性 | JDK Future | Netty Future | Netty Promise |
|---|---|---|---|
| 获取结果 | ✅ get() 阻塞获取 | ✅ get() 阻塞获取✅ getNow() 非阻塞获取 | ✅ 同 Netty Future |
| 状态查询 | ✅ isDone()✅ isCancelled() | ✅ isDone()✅ isCancelled()✅ isSuccess()✅ cause() | ✅ 同 Netty Future |
| 回调机制 | ❌ 不支持 | ✅ addListener() | ✅ 同 Netty Future |
| 设置结果 | ❌ 不支持 | ❌ 不支持 | ✅ setSuccess()✅ setFailure()✅ trySuccess()✅ tryFailure() |
| 阻塞等待 | ✅ get() | ✅ await()✅ sync() | ✅ 同 Netty Future |
3. 使用场景
JDK Future:
- 简单的异步任务
- 不需要回调机制
- 使用 JDK 原生线程池
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(() -> "结果");
String result = future.get(); // 阻塞获取
Netty Future:
- Netty 框架中的异步操作(只读取结果)
- 需要回调机制
- 需要非阻塞查询
ChannelFuture future = bootstrap.connect("localhost", 8080);
future.addListener(f -> {
if (f.isSuccess()) {
System.out.println("连接成功");
}
});
Netty Promise:
- 需要手动设置异步任务的结果
- 自定义异步操作
- 需要在不同线程间传递结果
Promise<String> promise = eventLoop.newPromise();
promise.addListener(f -> System.out.println(f.getNow()));
// 在其他线程设置结果
new Thread(() -> promise.setSuccess("完成")).start();
4. 形象比喻总结
| 概念 | 比喻 | 角色 | 能力 |
|---|---|---|---|
| JDK Future | 取餐号码牌(基础版) | 顾客 | 只能站在窗口等待取餐 |
| Netty Future | 取餐号码牌(升级版) | 顾客 | 可以留电话号码,做好了通知你 |
| Netty Promise | 后厨订单单 | 厨师 | 可以标记订单完成或失败 |
五、实战建议
1. 什么时候使用 Future?
当你调用 Netty 的异步方法时,返回的就是 Future,你只需要:
// 场景 1:不关心结果,继续执行
bootstrap.connect("localhost", 8080);
// 场景 2:添加监听器处理结果
bootstrap.connect("localhost", 8080).addListener(future -> {
// 处理连接结果
});
// 场景 3:阻塞等待结果
ChannelFuture future = bootstrap.connect("localhost", 8080);
future.sync(); // 等待连接完成
2. 什么时候使用 Promise?
当你需要自己实现异步操作时:
public Promise<String> asyncOperation(EventLoop eventLoop) {
// 创建 Promise
Promise<String> promise = eventLoop.newPromise();
// 异步执行任务
new Thread(() -> {
try {
// 执行业务逻辑
String result = doSomething();
// 设置成功结果
promise.setSuccess(result);
} catch (Exception e) {
// 设置失败结果
promise.setFailure(e);
}
}).start();
// 立即返回 Promise
return promise;
}
// 使用
asyncOperation(eventLoop).addListener(future -> {
if (future.isSuccess()) {
System.out.println("结果:" + future.getNow());
}
});
3. 常见错误
错误 1:重复设置 Promise
Promise<String> promise = eventLoop.newPromise();
promise.setSuccess("第一次");
promise.setSuccess("第二次"); // ❌ 抛异常!
// 正确做法:使用 try 方法
if (!promise.trySuccess("第二次")) {
System.out.println("已经设置过了");
}
错误 2:在 IO 线程中阻塞
// ❌ 错误:在 Netty 的 IO 线程中调用 sync() 或 await()
public void channelActive(ChannelHandlerContext ctx) {
ChannelFuture future = ctx.writeAndFlush(msg);
future.sync(); // 可能导致死锁!
}
// ✅ 正确:使用监听器
public void channelActive(ChannelHandlerContext ctx) {
ctx.writeAndFlush(msg).addListener(future -> {
if (future.isSuccess()) {
// 处理成功
}
});
}
错误 3:忘记处理失败情况
// ❌ 错误:只处理成功情况
future.addListener(f -> {
System.out.println("结果:" + f.getNow()); // 如果失败,getNow() 返回 null
});
// ✅ 正确:检查是否成功
future.addListener(f -> {
if (f.isSuccess()) {
System.out.println("结果:" + f.getNow());
} else {
System.err.println("失败:" + f.cause().getMessage());
}
});
六、总结
- JDK Future:只能阻塞获取结果,没有回调功能,结果不可写
- Netty Future:在 JDK Future 基础上增加了回调功能(
addListener()),但结果仍然不可写 - Netty Promise:在 Netty Future 基础上增加了结果可写功能(
setSuccess()/setFailure())
记住这个关系:Promise 是可写的 Future,Future 是增强的 JDK Future。
在 Netty 中:
- 调用框架方法时,返回的是 Future(你只能读)
- 自己实现异步逻辑时,创建 Promise(你可以写)
希望这篇文章能帮助你理解 Netty 中的 Future 和 Promise!如果还有疑问,欢迎留言讨论。
528



被折叠的 条评论
为什么被折叠?



