深入理解JDK中的异步编程的利器:Future和CompletableFuture

深入理解JDK中的异步编程的利器:Future和CompletableFuture

在日常开发中,异步编程变得更加重要。Java通过FutureCompletableFuture提供了强大的工具来处理异步任务。本文将深入探讨这两个类的技术细节,并通过实际案例展示它们的使用。

 一、Future

Future是Java 5引入的一个接口,用于表示异步计算的结果。它允许我们提交一个任务,并在未来的某个时间点获取该任务的结果。

 核心方法

public interface Future<V> {
    // 尝试取消任务的执行
    boolean cancel(boolean mayInterruptIfRunning);
    
    // 判断任务是否被取消
    boolean isCancelled();
    
    // 判断任务是否完成
    boolean isDone();
    
    // 获取任务的结果,如果任务未完成,则阻塞直到任务完成
    V get();
    
    // 在指定时间内获取任务的结果,超时则抛出TimeoutException
    V get(long timeout, TimeUnit unit);
}

 使用案例

public class FutureTest {
    public static void test() throws ExecutionException, InterruptedException {
        ExecutorService executor = Executors.newSingleThreadExecutor();

        Future<Integer> future = executor.submit(() -> {
            Thread.sleep(3000);
            return 1;
        });

        System.out.println("Task submitted");
         // 阻塞直到任务完成
        System.out.println("Task Result: " + future.get()); 
        executor.shutdown();
    }
}

在上面的例子中,我们提交了一个任务,该任务在3秒后返回结果。future.get()会阻塞当前线程,直到子线程的任务完成并返回结果。

 局限性

  • 回调缺失:不支持任务完成后的回调逻辑。

  • 组合困难:多个Future的依赖关系需手动处理。

  • 异常处理:需捕获InterruptedException和ExecutionException

 二、CompletableFuture

CompletableFuture是Java 8引入的一个类,它实现了Future接口,并提供了更强大的功能,如任务组合、异常处理等。CompletableFuture可以手动完成,并且支持回调式的编程风格。

  • 链式调用:支持thenApply()、thenAccept()等链式操作。

  • 组合操作:支持thenCombine()、allOf()实现多任务协同。

  • 异常传播:提供exceptionally()优雅的异常处理方式。

  • 响应式特征:支持thenRunAsync()非阻塞回调。

 核心方法

public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {

    // 异步执行一个没有返回值的任务。使用默认线程池。
    public static CompletableFuture<Void> runAsync(Runnable runnable) {
        return asyncRunStage(ASYNC_POOL, runnable);
    }

    // 异步执行一个没有返回值的任务。使用指定线程池。
    public static CompletableFuture<Void> runAsync(Runnable runnable,

                                                   Executor executor) {
        return asyncRunStage(screenExecutor(executor), runnable);
    }

    // 异步执行一个有返回值的任务。使用默认线程池。
    public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
        return asyncSupplyStage(ASYNC_POOL, supplier);
    }

    // 异步执行一个有返回值的任务。使用指定线程池。
    public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,

                                                       Executor executor) {
        return asyncSupplyStage(screenExecutor(executor), supplier);
    }

    // 在当前任务完成后,对结果进行处理。
    public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) {
        return uniApplyStage(null, fn);
    }

    // 在当前任务完成后,消费结果。
    public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {
        return uniAcceptStage(null, action);
    }

    // 在当前任务完成后,执行下一个操作。
    public CompletableFuture<Void> thenRun(Runnable action) {
        return uniRunStage(null, action);
    }

    // 处理任务执行过程中的异常。
    public CompletableFuture<T> exceptionally(
        Function<Throwable, ? extends T> fn) {
        return uniExceptionallyStage(null, fn);
    }

}

 使用案例

1、基本用法

    下面示例,我们提交了一个异步任务,该任务在3秒后返回结果。

public class FutureTest {
    public static void test() throws ExecutionException, InterruptedException {
        // 创建一个异步任务
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
        // 模拟业务逻辑耗时
        Thread.sleep(3000); 
        return "Hello, CompletableFuture!";
      });

      // 获取异步任务的结果
      String result = future.join(); 
      System.out.println("异步任务结果:" + result);
    }
}

    2、超时控制

    CompletableFuture.supplyAsync(() -> longTask())
        .orTimeout(3, TimeUnit.SECONDS)
        .exceptionally(ex -> "Timeout!");
    

    3、自定义线程池

    Executor asyncPool = Executors.newFixedThreadPool(4);
    CompletableFuture.runAsync(() -> asyncTask(), asyncPool);
    

    4、结果处理 & 异常处理

    future.whenComplete((result, throwable) -> {
        if (throwable != null) {
            log.error("Error occurred", throwable);
        }
    });
    
    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
                System.out.println("执行结束!");
                int i = 12 / 0;
                return "test";
            }).exceptionally(new Function<Throwable, String>() {
                @Override
                public String apply(Throwable throwable) {
                    System.out.println(throwable);
                    return "异常";
                }
            });
            
    // 执行结束!
    // java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero

    5、组合多个异步任务

    通过thenCombine支持将多个异步任务组合在一起执行。

    public class CompletableFutureCombineExample {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return 10;
            });
    
            CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return 20;
            });
    
            CompletableFuture<Integer> combinedFuture = future1.thenCombine(future2, Integer::sum);
    
            System.out.println("Combined Result: " + combinedFuture.get());
        }
    }
    

    6、回调机制

    CompletableFuture提供了强大的回调机制,允许我们在任务完成时执行特定的操作。以下是一个示例:

    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
        System.out.println("异步任务执行中...");
        return "Hello, CompletableFuture!";
    }).thenAccept(result -> {
        System.out.println("任务完成,回调结果:" + result);
    });
    

    在这个例子中,我们使用了thenAccept()方法来注册一个回调函数,当异步任务完成时,回调函数会被自动调用。

    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
              System.out.println("执行结束!");
              return "test";
     }).thenCompose(new Function<String, CompletionStage<String>>() {
              @Override
              public CompletionStage<String> apply(String s) {
                 System.out.println("执行完成!");
                 return CompletableFuture.supplyAsync(() -> s + " yang");
              }
    });
    

    在这个例子中,我们使用了thenCompose()方法来注册一个回调函数,当异步任务完成时,回调函数会被自动调用。

    7、链式调用

    在这个例子中,我们首先通过 supplyAsync() 创建了一个异步任务,然后使用 thenApply() 对任务结果进行处理,最后使用 thenAccept() 输出最终结果。这种链式调用的方式使得代码更加清晰和易读。

    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
     System.out.println("Step 1: 获取数据");
     return"Hello";
    }).thenApply(result -> {
     System.out.println("Step 2: 处理数据");
     return result + ", CompletableFuture!";
    }).thenAccept(result -> {
        System.out.println("Step 3: 输出结果");
     System.out.println(result);
    });

    8、任务交互

    8.1 applyToEither有返回值

        通过该方法,对两个线程Task进行比较。哪个线程获得执行结果的,就执行下一步操作。

    CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
                int number = new Random().nextInt(10);
                System.out.println("第一阶段 start:" + number);
                try {
                    TimeUnit.SECONDS.sleep(number);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("第一阶段 end:" + number);
                return number;
            });
    
            CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
                int number = new Random().nextInt(10);
                System.out.println("第二阶段 start:" + number);
                try {
                    TimeUnit.SECONDS.sleep(number);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("第二阶段 end:" + number);
                return number;
            });
    
            future1.applyToEither(future2, new Function<Integer, Object>() {
                @Override
                public Object apply(Integer integer) {
                    System.out.println("最快结果:" + integer);
                    returninteger * 2;
                }
            });
    
    // 结果
    // 第一阶段 start:3
    // 第二阶段 start:1
    // 第二阶段 end:1
    // 最快结果:1
    // 第一阶段 end:3
    

    8.2 runAfterEither无返回值

    该方法对两个线程比较,有任何一个Task执行完成,就进行下一步操作,不关心各个任务的运行结果。

     CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("第一阶段:1");
                return 1;
            });
    
            CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("第二阶段:2");
                return 2;
            });
    
            future1.runAfterEither(future2, new Runnable() {
                @Override
                public void run() {
                    System.out.println("已经有一个任务完成了");
                }
            });
    
    // 结果
    // 第一阶段:1
    // 已经有一个任务完成了
    // 第二阶段:2
    

    8.3 runAfterBoth无返回值

    该方法对两个线程比较,两个Task全部执行完成,才进行下一步操作,不关心各个任务的运行结果。

     CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("第一阶段:1");
                return 1;
            });
    
            CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("第二阶段:2");
                return 2;
            });
    
            future1.runAfterBoth(future2, new Runnable() {
                @Override
                public void run() {
                    System.out.println("上面两个任务都执行完成了");
                }
    });
    
    // 结果
    // 第一阶段:1
    // 第二阶段:2
    // 上面两个任务都执行完成了
    

    8.4 anyOf有返回值

    当其中的任何一个完成时,方法返回。

            Random random = new Random();
            CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
                try {
                    TimeUnit.SECONDS.sleep(random.nextInt(5));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return"1";
            });
    
            CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
                try {
                    TimeUnit.SECONDS.sleep(random.nextInt(1));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return"2";
            });
            CompletableFuture<Object> result = CompletableFuture.anyOf(future1, future2);
            System.out.println(result.get());
    
    // 结果
    // 2

    8.5 allOf无返回值

    当其中的所有Task完成时,方法返回拿到结果。

     CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("task1 完成!");
                return"task1 完成!";
            });
    
            CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
                System.out.println("task2 完成!");
                return"task2 完成!";
            });
    
            CompletableFuture<Void> combindFuture = CompletableFuture.allOf(future1, future2);
            try {
                combindFuture.get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
    
    // 结果
    // task2 完成!
    // task1 完成!

     三、总结

    FutureCompletableFuture是Java中用于异步编程的重要工具。虽然Future提供了基本的异步任务支持,但它的功能相对有限。CompletableFuture作为Future的升级版,提供了更强大的功能,如链式调用、回调机制、异常处理等。通过合理使用CompletableFuture,我们可以编写出更加高效、清晰和易维护的异步代码。

    在实际开发中,CompletableFuture的应用场景非常广泛,例如异步加载数据、组合多个异步任务、实现复杂的业务流程等。掌握CompletableFuture的使用方法,将有助于我们更好地应对高并发和性能敏感的应用场景。

    希望本文能够帮助你更好地理解和使用FutureCompletableFuture。如果你对这些内容有任何疑问或建议,欢迎在评论区留言讨论。

    获取更多技术文章和面试秘籍,可以关注VX 公粽号:小鱼修炼笔记

    深入理解JDK中的异步编程的利器:Future和CompletableFuture

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值