CompletableFuture多线程

        CompletableFuture 是 Java 8 引入的一个类,位于 java.util.concurrent 包中。它是 Future 接口的一个实现,但提供了更多的功能和灵活性来处理异步计算。相比于传统的 Future,CompletableFuture 允许以非阻塞的方式编排和组合异步任务,并且可以通过链式调用、异常处理等功能简化异步编程。

一、创建异步任务及获取

1.supplyAsync创建异步任务

        supplyAsync会返回一个返回值,可以使用get方法去获取,返回值类型需要在泛型中指定。线程池如果不指定线程池则会使用默认线程池

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
                                                       Executor executor);

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);

使用:

        //newFixedThreadPool‌是Java中一个常用的线程池类
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        //supplyAsync有返回值,runAync没有返回值
        CompletableFuture<String> task= CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName());
            return Thread.currentThread().getName();
        },executorService);

        //获取返回值
        System.out.println(task.get());
        //使用完毕后要关闭线程池
        executorService.shutdown();
2.runAsync创建异步任务

        runAsync是一个无返回值的异步任务。

    public static CompletableFuture<Void> runAsync(Runnable runnable,
                                                   Executor executor);

    public static CompletableFuture<Void> runAsync(Runnable runnable);

使用:

        ExecutorService executorService = Executors.newFixedThreadPool(10);

        CompletableFuture<Void> task = CompletableFuture.runAsync(()->{
            System.out.println(Thread.currentThread().getName());
        },executorService);

        executorService.shutdown();
3.配合supplyAsync,获取任务结果的方法
// 如果完成则返回结果,否则就抛出具体的异常
public T get() throws InterruptedException, ExecutionException 
 
// 最大时间等待返回结果,否则就抛出具体异常
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
 
// 完成时返回结果值,否则抛出unchecked异常。为了更好地符合通用函数形式的使用,如果完成此 CompletableFuture所涉及的计算引发异常,则此方法将引发unchecked异常并将底层异常作为其原因
public T join()
 
// 如果完成则返回结果值(或抛出任何遇到的异常),否则返回给定的 valueIfAbsent。
public T getNow(T valueIfAbsent)
 
// 如果任务没有完成,返回的值设置为给定值
public boolean complete(T value)
 
// 如果任务没有完成,就抛出给定异常
public boolean completeExceptionally(Throwable ex) 

二、任务编排处理

 即调用主体taskA执行完后执行传参中的taskB。

thenApply和thenApplyAsync

        任务类型:Function<T, R>

        有入参,有返回值

thenAccept和thenAcceptAsync

        任务类型:Consumer<T>

        有入参,无返回值

thenRun和thenRunAsync

        任务类型:

        无入参,无返回值       

1.thenApply和thenApplyAsync

        thenApply 和 thenApplyAsync 都是 CompletableFuture 类中的方法,用于在前一个阶段完成时对结果进行衔接处理。然而,它们之间有一些关键的区别,主要体现在执行方式和线程使用上:

使用:        

        Supplier<U> 的U为返回值类型,而 Function<T, R> 的T为传参类型,R为返回值类型

        //newFixedThreadPool‌是Java中一个常用的线程池类
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        //创建任务内容
        //Supplier<U> 是一个没有输入参数但能返回结果的函数式接口,而 Function<T, R> 则是一个接受一个输入参数并返回结果的接口。
        Supplier<Integer> taskA= () ->{
            Integer i = 1;
            System.out.println(Thread.currentThread().getName()+"||"+i);
            return i;
        };

        Function<Integer,String> taskB= s ->{
            String i = s.toString()+1;
            System.out.println(Thread.currentThread().getName()+"||"+i);
            return i+"1";
        };
        //执行任务
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(taskA,executorService);
        sleep(1);
        //completableFuture.thenApply(taskB);
        CompletableFuture<String> result= completableFuture.thenApplyAsync(taskB,executorService);
        System.out.println("result:"+result.get());

        executorService.shutdown();

2.thenAccept和thenAcceptAsync

        thenAccept 和 thenAcceptAsync 都是 CompletableFuture 类中用于处理异步计算结果的方法,但它们在执行方式和线程使用上有所不同。这两个方法主要用于当你对前一个 CompletableFuture 的结果感兴趣,但不需要返回新的结果(即返回 void)时。

        //newFixedThreadPool‌是Java中一个常用的线程池类
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        //创建任务内容
        //Supplier<U> 是一个没有输入参数但能返回结果的函数式接口,而 Consumer<T> 则是一个接受一个输入参数的接口。
        Supplier<Integer> taskA= () ->{
            Integer i = 1;
            System.out.println(Thread.currentThread().getName()+"||"+i);
            return i;
        };

        Consumer<Integer> taskB= s ->{
            String i = s.toString()+1;
            System.out.println(Thread.currentThread().getName()+"||"+i);
        };
        //执行任务
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(taskA,executorService);
        sleep(1);
        //completableFuture.thenAccept(taskB);
        completableFuture.thenAcceptAsync(taskB,executorService);

        executorService.shutdown();

3.thenRun和thenRunAsync

        thenRun 和 thenRunAsync 是 CompletableFuture 类中提供的两种方法,用于在当前的 CompletableFuture 完成后执行一个Runnable任务。他们无需参数传入且无返回值:

        //newFixedThreadPool‌是Java中一个常用的线程池类
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        //创建任务内容
        //Supplier<U> 是一个没有输入参数但能返回结果的函数式接口,而 Runnable则是无参无返回值的接口。
        Supplier<Integer> taskA= () ->{
            Integer i = 1;
            System.out.println(Thread.currentThread().getName()+"||"+i);
            return i;
        };

        Runnable taskB= () ->{
            String i = "2";
            System.out.println(Thread.currentThread().getName()+"||"+i);
        };
        //执行任务
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(taskA,executorService);
        sleep(1);
        //completableFuture.thenRun(taskB);
        completableFuture.thenRunAsync(taskB,executorService);

        executorService.shutdown();
4.then**和then**Async的不同点

then**

  • 同步执行:当调用then**方法时,提供的函数会在当前的 CompletableFuture 完成时,在同一个线程中同步执行。
  • 不指定执行器:由于它是同步执行的,因此不需要提供一个 Executor 来决定在哪一个线程中运行该函数。

then**Async

  • 异步执行:与 then**不同,then**Async 提供的函数将在不同的线程中异步执行。这使得它可以避免阻塞主线程,特别是当操作可能需要一些时间完成时。
  • 可指定执行器:你可以选择性地提供一个 Executor 来控制函数将在哪个线程池中执行。如果你没有提供执行器,则会使用 ForkJoinPool.commonPool() 作为默认的执行器。

三、多任务组合处理

1.thenCombine、thenAcceptBoth 和 runAfterBoth

        thenCombine、thenAcceptBoth 和 runAfterBoth 是 CompletableFuture 类中用于组合两个 CompletableFuture 的方法。它们允许你以不同的方式处理两个异步操作的结果。如果有三个及以上,需要组合使用 thenCombine或thenAcceptBoth或runAfterBoth。

thenCombine

  • 功能:当两个 CompletableFuture 都完成后,使用提供的 BiFunction 将它们的结果结合起来,并返回一个新的 CompletableFuture。
  • 返回类型:返回一个新的 CompletableFuture<U>,其中 U 是 BiFunction 的结果类型。
  • 适用场景:当你需要等待两个异步操作都完成,并且想要根据这两个操作的结果进行一些计算或创建新的结果时非常有用。

thenAcceptBoth

  • 功能:当两个 CompletableFuture 都完成后,提供一个 BiConsumer 来处理它们的结果,但不返回任何结果(即返回 CompletableFuture<Void>)。
  • 返回类型:返回一个 CompletableFuture<Void>,因为它不产生新的结果。
  • 适用场景:当你需要等待两个异步操作都完成,并且想要基于这两个操作的结果执行某些操作(如日志记录、更新UI等),但不需要生成新的结果时非常有用。

runAfterBoth

  • 功能:当两个 CompletableFuture 都完成后,运行一个 Runnable,不接收任何参数也不返回任何结果(即返回 CompletableFuture<Void>)。
  • 返回类型:返回一个 CompletableFuture<Void>,因为它只是执行某个动作而不产生新的结果。
  • 适用场景:当你需要在两个异步操作都完成后执行某些动作(如资源清理、发送通知等),但不需要使用这两个操作的结果时非常有用。

使用:

        //newFixedThreadPool‌是Java中一个常用的线程池类
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        CompletableFuture<Integer> taskA = CompletableFuture.supplyAsync(() ->{
            Integer i = 1;
            System.out.println(Thread.currentThread().getName()+"||"+i);
            return i;
        },executorService);

        CompletableFuture<String> taskB = CompletableFuture.supplyAsync(() ->{
            String i = "2";
            System.out.println(Thread.currentThread().getName()+"||"+i);
            return i;
        });
        
        //thenAcceptBoth
        taskA.thenAcceptBoth(taskB,(resultA,resultB)->{
          System.out.println(resultA.toString()+resultB);
        });
        
        //runAfterBoth
        taskA.runAfterBoth(taskB,()->{
          System.out.println("All Complete!");
        });
        
        //thenCombine
        CompletableFuture<String> result = taskA.thenCombine(taskB,(resultA,resultB)->{
            return resultA.toString()+resultB;
        });
        System.out.println("result:"+result.get());

        executorService.shutdown();

2.applyToEither、acceptEither和runAfterEither

        applyToEither、acceptEither 和 runAfterEither 是 CompletableFuture 类中用于处理竞争条件的方法。它们允许你在多个 CompletableFuture 中任意一个完成时执行特定的操作,而不需要等待所有的 CompletableFuture 都完成。

applyToEither

  • 功能:当提供的两个 CompletableFuture 中任意一个完成时,使用提供的 Function 对其结果进行转换,并返回一个新的 CompletableFuture。
  • 返回类型:返回一个新的 CompletableFuture<U>,其中 U 是 Function 的结果类型。
  • 适用场景:当你有多个异步操作,只需要其中一个的结果来生成新的结果时非常有用。

acceptEither

  • 功能:当提供的两个 CompletableFuture 中任意一个完成时,提供一个 Consumer 来处理其结果,但不返回任何结果(即返回 CompletableFuture<Void>)。
  • 返回类型:返回一个 CompletableFuture<Void>,因为它不产生新的结果。
  • 适用场景:当你只需要处理两个 CompletableFuture 中任意一个的结果,而不需要生成新的结果时非常有用。

runAfterEither

  • 功能:当提供的两个 CompletableFuture 中任意一个完成时,运行一个 Runnable,不接收任何参数也不返回任何结果(即返回 CompletableFuture<Void>)。
  • 返回类型:返回一个 CompletableFuture<Void>,因为它只是执行某个动作而不产生新的结果。
  • 适用场景:当你希望在两个 CompletableFuture 中任意一个完成后执行某些操作(如资源清理、发送通知等),但不需要使用这些操作的结果时非常有用。

使用:

        //newFixedThreadPool‌是Java中一个常用的线程池类
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        CompletableFuture<String> taskA = CompletableFuture.supplyAsync(() ->{
            String i = "1";
            System.out.println(Thread.currentThread().getName()+"||"+i);
            return i;
        },executorService);

        CompletableFuture<String> taskB = CompletableFuture.supplyAsync(() ->{
            String i = "2";
            System.out.println(Thread.currentThread().getName()+"||"+i);
            return i;
        },executorService);

        //acceptEither
        taskA.acceptEither(taskB,(resultOne)->{
            System.out.println("resultOne:"+resultOne);
        });

        //runAfterEither
        taskA.runAfterEither(taskB,()->{
            System.out.println("One Complete!");
        });

        //applyToEither
        CompletableFuture<String> result = taskA.applyToEither(taskB,(resultOne)->{
            return resultOne;
        });
        System.out.println("result:"+result.get());

        executorService.shutdown();

四、针对多任务的异常处理

1.whenComplete和handle

        whenComplete 和 handle 都是 CompletableFuture 类中用于处理任务完成后的回调操作的方法,对比上述编排方法,可以处理异常,whenComplete无返回值,handle会返回一个CompleteFuture<?>

whenComplete使用:

CompletableFuture.supplyAsync(() -> {
    return "Hello";
}).whenComplete((result, exception) -> {
    if (exception != null) {
        System.out.println("Exception occurred: " + exception.getMessage());
    } else {
        System.out.println("Result is: " + result);
    }
});

handle使用:

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // 模拟异常
    throw new RuntimeException("Test Exception");
}).handle((result, exception) -> {
    if (exception != null) {
        System.out.println("Caught exception: " + exception.getMessage());
        return "Default Value";
    } else {
        return result;
    }
});

future.thenAccept(result -> System.out.println("Final Result: " + result));

五、阻塞进程等待异步处理

1.allOf和anyOf

        CompletableFuture.allOf 和 CompletableFuture.anyOf 是 Java 中用于组合多个 CompletableFuture 的静态方法,它们提供了不同的方式来等待这些 CompletableFuture 的完成。可以处理异常。

CompletableFuture.allOf

  • 功能:allOf 方法接收一个 CompletableFuture<?>... cfs 可变参数列表(即一个或多个 CompletableFuture),并返回一个新的 CompletableFuture<Void>,这个新的 CompletableFuture 会在所有提供的 CompletableFuture 都完成后才完成。
  • 返回类型:返回的 CompletableFuture<Void> 不包含任何结果值。如果需要获取每个 CompletableFuture 的结果,你需要单独调用各自的 join() 或 get() 方法。
  • 适用场景:当你需要等待一组异步任务全部完成后再进行后续处理时非常有用

使用:

        //newFixedThreadPool‌是Java中一个常用的线程池类
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        CompletableFuture<String> taskA = CompletableFuture.supplyAsync(() ->{
            String i = "1";
            try {
                sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"||"+i);
            return i;
        },executorService);

        CompletableFuture<String> taskB = CompletableFuture.supplyAsync(() ->{
            String i = "2";
            System.out.println(Thread.currentThread().getName()+"||"+i);
            return i;
        },executorService);

        CompletableFuture<String> taskC = CompletableFuture.supplyAsync(() ->{
            String i = "3";
            System.out.println(Thread.currentThread().getName()+"||"+i);
            return i;
        },executorService);


        //allOf
        CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(taskA,taskB,taskC);
        allOfFuture.get();
        System.out.println("阻塞后执行语句");

        executorService.shutdown();

CompletableFuture.anyOf

  • 功能:anyOf 方法接收一个 CompletableFuture<?>... cfs 可变参数列表,并返回一个新的 CompletableFuture<Object>,这个新的 CompletableFuture 会在任意一个提供的 CompletableFuture 完成后就立即完成。
  • 返回类型:返回的 CompletableFuture<Object> 包含第一个完成的 CompletableFuture 的结果。需要注意的是,由于Java泛型的原因,结果被声明为 Object 类型,因此你可能需要进行类型转换。
  • 适用场景:当你有一组异步任务,并且只需要其中一个任务的结果时非常有用。例如,实现一种“竞态”机制,哪个任务先完成就使用哪个任务的结果。

使用:

        //newFixedThreadPool‌是Java中一个常用的线程池类
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        CompletableFuture<String> taskA = CompletableFuture.supplyAsync(() ->{
            String i = "1";
            try {
                sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"||"+i);
            return i;
        },executorService);

        CompletableFuture<String> taskB = CompletableFuture.supplyAsync(() ->{
            String i = "2";
            System.out.println(Thread.currentThread().getName()+"||"+i);
            return i;
        },executorService);

        CompletableFuture<String> taskC = CompletableFuture.supplyAsync(() ->{
            String i = "3";
            try {
                sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"||"+i);
            return i;
        },executorService);


        //allOf
        CompletableFuture<Object> allOfFuture = CompletableFuture.anyOf(taskA,taskB,taskC);
        allOfFuture.get();
        System.out.println("阻塞后执行语句");

        executorService.shutdown();
2.runAfterBoth/runAfterEither和allOf/anyOf不同点
  • runAfterBoth 和 runAfterEither 主要是为了在特定条件下(两个都完成或任一完成)执行一些副作用操作(如日志记录),并不直接提供任何结果。
  • allOf 和 anyOf 则用于组合多个 CompletableFuture 并基于这些组合的状态(所有完成或任一完成)来决定下一步的动作,前者等待所有完成后者则在任意一个完成时即刻响应。

### 使用 CompletableFuture 实现多线程环境中的事务管理 在 Java 中,`CompletableFuture` 提供了一种灵活的方式来组合异步操作并处理它们之间的依赖关系。为了确保多个 `CompletableFuture` 调用作为一个整体成功完成或全部回滚,在多线程环境中实现事务管理的最佳实践如下: #### 1. 组合多个 CompletableFutures 并等待所有结果 通过收集所有的 `CompletableFuture<Void>` 到列表中,并最终调用 `allOf()` 方法来创建一个新的 `CompletableFuture` 对象,该对象将在所有给定的 completable futures 完成后完成。 ```java import java.util.List; import java.util.concurrent.CompletableFuture; public class TransactionManager { public static void executeTransaction(List<Runnable> tasks) throws Exception { List<CompletableFuture<Void>> futures = tasks.stream() .map(task -> CompletableFuture.runAsync(task)) .toList(); try { // Wait for all futures to complete. CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); } catch (Exception e) { throw new RuntimeException("Transaction failed", e); } } } ``` 这种方法允许在一个地方捕获任何异常,并决定如何响应失败的任务——无论是抛出异常还是尝试补偿措施[^2]。 #### 2. 添加错误恢复机制 当某个阶段发生错误时,可以通过定义 `.exceptionally()` 或者更复杂的链式回调来进行补救工作。这有助于保持系统的稳定性和一致性状态。 ```java // Example of adding error handling with exceptionally(). futures.forEach(cf -> cf.exceptionally(ex -> { System.err.println("Error occurred during task execution: " + ex.getMessage()); return null; // or some default value depending on your use case }) ); ``` 对于需要更强一致性的场景,则应考虑引入分布式事务协调器或其他形式的日志记录/持久化方案以支持幂等性操作[^3]。 #### 3. 使用虚拟线程优化性能 如果应用程序涉及大量并发任务,可以利用 JDK 21 引入的新特性——虚拟线程(Project Loom)。相比于传统的固定大小线程池,虚拟线程提供了更好的资源利用率和更低的上下文切换开销。不过需要注意的是,由于每个任务都会启动独立的轻量级线程实例,因此应当谨慎评估潜在的内存占用情况以及是否有必要实施额外的对象缓存策略[^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值