HOOOS

Java并发编程进阶:Future与CompletableFuture深度解析与实战

0 67 爱编程的程序猿 Java并发编程CompletableFuture
Apple

Java并发编程进阶:Future与CompletableFuture深度解析与实战

你好呀!今天咱们来聊聊Java并发编程里的两个“狠角色”:FutureCompletableFuture。别担心,我会尽量用大白话给你讲明白,保证你能听懂,还能用得上。

1. 为啥要用Future和CompletableFuture?

你想啊,咱们平时写程序,很多时候都是一步一步来的,就像这样:

// 1. 先煮饭
boilRice();
// 2. 再炒菜
cookVegetables();
// 3. 最后吃饭
eat();

煮饭的时候,你就只能干等着,啥也干不了。要是煮饭要半小时,你就得干等半小时!这多浪费时间啊!

在Java里,这种情况就叫“同步阻塞”。“同步”就是按顺序一步步来,“阻塞”就是上一步没做完,下一步就得等着。

那有没有办法让煮饭、炒菜同时进行呢?当然有!这就需要用到多线程了。

// 创建一个线程来煮饭
Thread boilRiceThread = new Thread(() -> boilRice());
boilRiceThread.start();

// 同时,主线程可以去炒菜
cookVegetables();

// ... 等煮饭线程结束,再吃饭
boilRiceThread.join(); // 等待煮饭线程完成
eat();

这样,煮饭和炒菜就可以同时进行了,大大节省了时间。但是,新的问题又来了:你怎么知道饭煮好了呢?boilRiceThread.join() 会一直阻塞,直到煮饭线程完成。如果我想在煮饭的同时,再做点别的事情呢?

这时候,Future 就闪亮登场了!

2. Future:异步计算的“期货”

Future,顾名思义,就是“未来”。它代表了一个异步计算的“未来结果”。你可以把它想象成一张“期货合约”,你现在拿不到结果,但是将来某个时候,你可以凭这张“合约”去取结果。

怎么用呢?看代码:

// 创建一个线程池
ExecutorService executor = Executors.newFixedThreadPool(2);

// 提交一个煮饭的任务,返回一个Future对象
Future<String> boilRiceFuture = executor.submit(() -> {
    // 模拟煮饭耗时
    Thread.sleep(3000);
    return "香喷喷的米饭";
});

// 同时,主线程可以去炒菜
cookVegetables();

// 想知道饭好了没?问问Future
if (boilRiceFuture.isDone()) {
    // 饭好了,可以取结果了
    String rice = boilRiceFuture.get();
    System.out.println(rice);
} else {
    // 饭还没好,可以先干点别的
    System.out.println("饭还没好,先喝口水...");
}

// ... 等需要吃饭的时候,再来取结果
String rice = boilRiceFuture.get(); // 如果饭还没好,这里会阻塞
eat(rice);

// 关闭线程池
executor.shutdown();

看到了吧?Future 就像一个“占位符”,你先拿着,等需要的时候再来取结果。isDone() 方法可以用来检查任务是否完成,get() 方法用来获取结果(如果任务还没完成,get() 方法会阻塞)。

3. Future的局限性

Future 虽然好用,但也有一些局限性:

  • 不能手动完成: Future 表示的任务一旦开始,你就没办法手动设置它的结果,只能被动等待它完成。
  • 阻塞式获取结果: get() 方法是阻塞的,如果任务还没完成,你就得一直等着。
  • 不支持异步回调: Future 不能设置回调函数,当任务完成后,你没办法自动得到通知。
  • 难以组合多个 Future 如果你有多个 Future,想把它们的结果组合起来,或者按照某种顺序执行它们,用 Future 会很麻烦。

为了解决这些问题,Java 8 引入了 CompletableFuture

4. CompletableFuture:更强大的异步编程工具

CompletableFuture,你可以把它理解为“可完成的Future”,或者“增强版的Future”。它不仅提供了 Future 的所有功能,还增加了许多强大的新特性,让异步编程更加灵活、高效。

4.1 手动完成

CompletableFuture<String> future = new CompletableFuture<>();

// 在未来的某个时刻,手动设置结果
future.complete("手动设置的结果");

// 获取结果
String result = future.get(); // result = "手动设置的结果"

4.2 异步回调

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // 模拟耗时操作
    Thread.sleep(3000);
    return "异步计算的结果";
});

// 当任务完成后,自动执行回调函数
future.thenAccept(result -> {
    System.out.println("收到结果:" + result);
});

// 主线程可以继续做其他事情
System.out.println("主线程继续执行...");

// ...

thenAccept 就是一个回调函数,当 future 完成时,它会自动被调用,你可以在回调函数里处理结果,而不需要主动去 get()

4.3 组合多个CompletableFuture

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");

// 将两个Future的结果组合起来
CompletableFuture<String> combinedFuture = future1.thenCombine(future2, (s1, s2) -> s1 + " " + s2);

// 获取组合后的结果
String result = combinedFuture.get(); // result = "Hello World"

thenCombine 可以将两个 CompletableFuture 的结果组合起来,形成一个新的 CompletableFuture

4.4 异常处理

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // 模拟一个异常
    if (true) {
        throw new RuntimeException("计算出错!");
    }
    return "正常结果";
});

// 处理异常
future.exceptionally(ex -> {
    System.out.println("发生异常:" + ex.getMessage());
    return "备用结果";
});

// 获取结果(如果发生异常,会得到备用结果)
String result = future.get(); // result = "备用结果"

exceptionally 可以让你处理 CompletableFuture 中发生的异常。

4.5 更多用法

CompletableFuture 还提供了很多其他强大的功能,比如:

  • thenApply:对 CompletableFuture 的结果进行转换。
  • thenCompose:将一个 CompletableFuture 的结果作为另一个 CompletableFuture 的输入。
  • allOf:等待多个 CompletableFuture 全部完成。
  • anyOf:只要多个 CompletableFuture 中有一个完成,就返回。
  • ......

5. 实战案例

说了这么多,咱们来个实战案例,看看 CompletableFuture 怎么用。

假设我们要开发一个电商网站,用户下单后,我们需要做以下几件事:

  1. 检查库存。
  2. 扣减库存。
  3. 生成订单。
  4. 发送短信通知用户。

这些操作可以并行执行,以提高效率。我们可以用 CompletableFuture 来实现:

// 检查库存
CompletableFuture<Boolean> checkStockFuture = CompletableFuture.supplyAsync(() -> {
    // 模拟检查库存
    System.out.println("检查库存...");
    return true; // 假设库存充足
});

// 扣减库存
CompletableFuture<Void> deductStockFuture = checkStockFuture.thenAcceptAsync(stockAvailable -> {
    if (stockAvailable) {
        // 模拟扣减库存
        System.out.println("扣减库存...");
    }
});

// 生成订单
CompletableFuture<String> createOrderFuture = checkStockFuture.thenApplyAsync(stockAvailable -> {
    if (stockAvailable) {
        // 模拟生成订单
        System.out.println("生成订单...");
        return "订单号:123456";
    } else {
        return null;
    }
});

// 发送短信
CompletableFuture<Void> sendNotificationFuture = createOrderFuture.thenAcceptAsync(orderId -> {
    if (orderId != null) {
        // 模拟发送短信
        System.out.println("发送短信通知用户,订单号:" + orderId);
    }
});

// 等待所有任务完成
CompletableFuture.allOf(deductStockFuture, sendNotificationFuture).join();

System.out.println("下单完成!");

在这个例子中,我们使用了 CompletableFuturesupplyAsyncthenAcceptAsyncthenApplyAsyncallOf 方法,将多个异步任务组合起来,形成一个完整的下单流程。这些任务可以并行执行,大大提高了程序的效率。

6. 总结

FutureCompletableFuture 是Java并发编程中非常重要的两个工具,它们可以帮助我们编写高效、响应迅速的异步程序。CompletableFutureFuture 的基础上,提供了更强大的功能,让异步编程更加灵活、方便。

希望这篇文章能帮助你理解 FutureCompletableFuture,并在实际开发中灵活运用它们。如果你还有什么问题,欢迎随时提问!

点评评价

captcha
健康